오늘은 함수 호출 규약에 대해서 알아보도록 하자, 우리가 알고 있는 함수 호츌 규약은 아래와 같이 4가지를 볼 수 있다.


1. __cdecl

2. __stdcall

3. __fastcall

4. __thiscall


Visual Studio 2010으로 확인을 한다면 아래와 같은 그림을 볼 수 있다.




이제 하나씩 서로의 차이를 알아보자.




1.__cdecl


C언어의 표균 함수 호출 규약이다. 파라미터는 오른쪽에서 왼쪽으로 스택을 통해 전달된다.

스택은 호출한 곳에서 스택을 정리한다. 

스택을 호출한 곳에서 정리함에 따라 함수 호출의 인자의 갯수가 무의미 한 장점이 있다.

즉 가변인자를 지원한다. 


void printf(char * dest, ...); 이런식의 함수를 많이 사용 했을 것이다. 

이런 것들과 비슷한 가변 인자를 지원할 수 있는 것이다. 



extern "c" int __cdecl CdeclFunc(int a, int b , int c)

{

printf("%d%d%d\n",a,b,c);

}


해당 함수를 리버싱 하여 스택의 순서를 보면 아래와 같이 보인다.


push 3
push 2
push 1
call CdeclFunc
add esp, 12



함수의 인자가 3개이다. 고로 오른쪽으로 부터 스택에 전달되니 push 3, push 2, push 1 순으로 스택에 들어간다. 


그런 다음에 함수를 호출하는 call FunA를 하게 된다. 


그 다음에 __cdecl의 특징인 함수를 호출한 자가 스택을 정리하는 코드가 나타난다.


add exp, 12 이다. 4바이트 인자 3개의 스택을 정리하는 코드이다. 




2.__stdcall


윈도우 API에서 사용하는 함수 호출 표준 규약이다. 파라미터는 오른쪽에서 왼쪽으로 전달 되며, 스택을 정리하는 곳은 

함수를 호출 당한곳에서 이루어 진다. 즉 함수가 종료 되면서 스택을 정리하는 것이다. 


extern "c" int __stdcall StdcallFunc(int a, int b , int c)

{

printf("%d%d%d\n",a,b,c);

}


위와 같은 함수를 리버싱한다면 아래와 같은 코드가 나온다.


push 3
push 2
push 1
call _StdcallFunc@12


함수의 인자가 3개이다. 고로 오른쪽으로 부터 스택에 전달되니 push 3, push 2, push 1 순으로 스택에 들어간다.

__cdecl 처럼 함수가 호출이 끝나고 존재하는 스택 정리 코드가 없다. 

함수 호출을 당한 자가 스택을 정리하기 떄문에 가변인자를 지원하지 못한다.


이것이 __stdcall의 특장점이다.

여기서 보면 코드 한줄 없어 지는것이 얼마나 이득이냐고 반문 할 수 있지만 함수가 많어지고 호출이 많이 지면 

실행 파일의 용량과 수행속도에서 차이가 나기 시작하는 것이다. 




3.__fastcall


이름을 보면 빠른 함수 호출이다. 말 그대로 빠른 함수 호출을 하기 위해서 고안된 방식이다. Windows X64 프로그래밍의 fastcall과는 다른 성격이지만 비슷한 면도 많다. 


x86 레지스터를 이용하여 함수 인자 값을 전달 하는 계념이다. ecx,edx를 이용하여 함수 인자 2개를 저장하고 

남어지 부분들은 스택에 저장하는 방식이다. 


CPU가 일처리하기 위해서 주 기억장치에서 그 값을 불러다 쓸경우 보다는 레지스터에서 바로 땡겨서 쓰는 편히 훨 씬 빠르기 떄문에 함수 인자가 2개일 경우 보다 빠르게 동작 할 수 있는 장점이 있다. 


스택 저장은 오른쪽에서 왼쪽으로 동일하다.


extern "c" int __fastcall FastcallFunc(int a, int b , int c)

{

printf("%d%d%d\n",a,b,c);

}


인자 값 3개인 함수를 리버싱 해보겠다. 아래와 같은 코드가 나타난다.


push 3
mov edx, 2
mov ecx, 1
call @FastcallFunc@12


함수의 인자가 3개이다. 고로 오른쪽으로 부터 스택에 전달되니 push 3 이 스택에 들어간다.

그리고 남어지 2개의 인자는 앞서 설명 한 것과 같이 각각 edx, ecx에 들어가게 된다.

그리고 나서 함수를 호출 하며 __stdcall과 같이 스택은 함수호출을 당한자가 진행하게 된다.


함수 호출을 당한 자가 스택을 정리하기 떄문에 가변인자를 지원하지 못한다.



4. __thiscall

그리고 C++로 넘어 오면서 생긴 특수한 함수 호출 규약이 있다. 바로 __thiscall이다. 

흔히 C++의 Class를 지원하면서 자신의 클래스의 접근을 하고자 한다면 this-> 이런 형식을 남발 하면서 사용하고 있을 것이다. 


해당 방식의 스택은 역시 오른쪽에서 왼쪽으로 전달하게 되어 있으며 스택 정리 또한 호출 당한 쪽에서 진행하게 된다. 

그리고 This를 하기 위해서는 자신의 클래스 포인터를 전달 해야되는데 바로 ecx를 통해서 자신의 클래스 포인터를 전달 하게 된다. 


class CCallConv

{

public :

int thisCall(int a, int b, int c);

};


CCallConv conv; 

int CallConv::thisCall(int a,int b int c)

{

return printf("%d%d%d\n",a,b,c);

}


위와 같은 클래스를 리버싱 하면 아래와 같은 코드가 나온다.

push 3
push 2
push 1
lea ecx, conv
call CCallConv::ThisCall


함수의 인자가 3개이다. 고로 오른쪽으로 부터 스택에 전달되니 push 3 이 스택에 들어간다.

그리고 자신의 클래스 포인터인 conv 를 ecx에 저장하여 전달 하게 된다 .



스택을 정리하는 방식에 따른 차이가 왜 있을까?

함수 호출 규약의 주된 차이점은 스택 정리 방식이다. 호출한 쪽에서 정리하는 방식과 호출을 당한 곳에서 정리하는 방법이 있다. 호출한 쪽에서 정리하는 방법의 장점은 자신이 파라미터로 전달한 인자의 개수를 정확히 알 수 있기 때문에 가변 인자를 지원할 수 있다는 것이다. 그렇다면 __cdecl을 제외한 나머지 방식은 왜 스택을 호출을 당한 곳에서 정리하는 것일까?

그 이유는 속도에 있다. x86 CPU의 경우 ret 어셈블리 명령어를 두 가지 형태로 지원한다. ret를 하면 단순히 스택에 저장된 복귀 주소로 리턴 한다. 하지만 ret imm16을 사용하면 imm16만큼 스택에서 팝 한 다음 꺼내진 복귀 주소로 리턴 한다. 위에서 살펴 보았듯이 __cdecl은 스택 정리를 위해서 add 명령어가 추가된다. 반면에 호출을 당한 곳에서 정리하는 방식은 리턴할 때에 ret imm16을 사용함으로써 add 명령어가 추가되지 않아도 된다. 또한 486을 기준으로 했을 때 ret와 ret imm16은 5클럭 사이클을 소모하는 동일한 명령어로 처리된다. 따라서 __cdecl보다는 다른 호출 규약이 근소하게 빠를 수 있다.


마지막으로 정리를 하자면 아래와 같은 표로 정리 할 수 있다.


호출 규약파라미터스택특징
__cdecl오른쪽 -> 왼쪽호출한 곳가변 인자 지원
__fastcall오른쪽 -> 왼쪽호출 당한 곳파라미터 두 개를 ecx, edx를 통해서 전달하기 때문에 두 개 이하의 인자를 가진 함수에 대해서 빠름
__stdcall오른쪽 -> 왼쪽호출 당한 곳Windows 표준 호출 규약
__thiscall오른쪽 -> 왼쪽호출 당한 곳ecx를 통해서 this 포인터를 전달 함


Posted by 최우림 -=HaeJuK=-
Study/DesignPatterns2010.03.19 16:13
목록..............

생성패턴(Creational Patterns)
-Abstract Factory 패턴
-Builder 패턴
-Factory Method 패턴
-Prototype 패턴
-Singleton 패턴

구조패턴(Structual Patterns)
-Adapter 패턴
-Bridge 패턴
-Composite 패턴
-Decorator 패턴
-Facade 패턴
-Flyweight 패턴
-Proxy 패턴

행위패턴(Behavioral Patterns)
-Chain of Responsibility 패턴
-Command 패턴
-Interpreter 패턴
-Iterator 패턴
-Mediator 패턴
-Memento 패턴
-Observer 패턴
-State 패턴
-Strategy 패턴
-Template Method 패턴
-Visitor 패턴
Posted by 최우림 -=HaeJuK=-
Study/CUDA2010.03.08 10:30

CUDA ("Compute Unified Device Architecture", 쿠다)는 그래픽 처리 장치(GPU)에서 수행하는 (병렬 처리) 알고리즘을 C 프로그래밍 언어를 비롯한 산업 표준 언어를 사용하여 작성할 수 있도록 하는 GPGPU 기술이다.

 

CUDA는 엔비디아가 개발해오고 있으며 이 아키텍처를 사용하려면 엔비디아 GPU와 특별한 스트림 처리 드라이버가 필요하다.

CUDA는 G8X GPU로 구성된 지포스 8 시리즈급 이상에서 동작한다.

엔비디아는 지포스 8 시리즈 상에 작성된 프로그램은 앞으로 개발될 엔비디아 비디오 카드에서도 별도의 프로그램 수정 없이 작동할 것이라고 선언했다. CUDA는 CUDA GPU 안의 명령셋과 대용량 병렬 처리 메모리를 접근할 수 있도록 해 준다.

 

개발자는 패스스케일 오픈64 C 컴파일러로 컴파일 된 '쿠다를 위한 C' (C언어를 엔비디아가 확장한 것) 를 사용하여 GPU 상에서 실행시킬 알고리듬을 작성할 수 있다. 쿠다 구조는 일련의 계산 인터페이스를 지원하며 이에는 OpenCL, DirectX Compute가 포함된다. 3자 개발 래퍼도 있는데, 파이썬, 포트란, 자바매트랩 등이 있다.

 

최신 드라이버는 모두 필요한 쿠다 콤포넌트를 담고 있다. 쿠다는 모든 엔비디아 GPU (G8X 시리즈 이후) 를 지원하며 이 대상에는 지포스, 콰드로, 테슬라 제품군이 포함된다. 엔비디아는 지포스 8 시리즈를 위해 개발된 프로그램들은 수정 없이 모든 미래의 엔비디아 비디오 카드에서 실행될 것이라고 선언하였다. 쿠다를 통해 개발자들은 쿠다 GPU 안 병렬 계산 요소 고유의 명령어 집합과 메모리에 접근할 수 있다. 쿠다를 사용하여 최신 엔비디아 GPU를 효과적으로 개방적으로 사용할 수 있다. 그러나 CPU와는 달리 GPU는 병렬 다수 코어 구조를 가지고 있고, 각 코어는 수천 스레드를 동시에 실행시킬 수 있다. 어떤 응용 프로그램이 이러한 종류의 구조에 적합하면, GPU는 커다란 성능 혜택을 입을 수 있다.

 

컴퓨터 게임 업계에서는 그래픽 랜더링에 덧붙여, 그래픽 카드를 게임 물리 계산 (파편, 연기, 불, 유체 등 물리 효과)에 사용되며, 예로는 피즈 엑스불렛이 있다. 쿠다는 그래픽이 아닌 응용 프로그램, 즉, 계산 생물학, 암호학, 그리고 다른 분야에서 10배 또는 그 이상의 속도 혜택을 가져왔다. 이 한 예는 보닉 분산 계산 클라이언트 이다.

 

쿠다는 저수준 API와 고수준 API 모두를 제공한다. 최초의 CUDA SDK는 2007년 2월 15일에 공개되었으며 마이크로소프트 윈도우즈리눅스를 지원했다. 맥 OS X지원은 2.0 버전에 추가되었다.

Posted by 최우림 -=HaeJuK=-
Study/Effective C++2010.03.08 10:27

합성(composition)이란, 어떤 타입의 객체들이 그와 다른 타입의 객체들을 포함하고 있을 경우에 성립하는 그 타입들 사이의 관계를 일컫습니다. 포함된 객체들을 모아서 이들을 포함한 다른 객체를 합성한다는 뜻인데, 다음과 같은 경우입니다.

class Address { …. };                                                // 누군가의 거주지

class PhoneNumber { …. };

 

class Person

{

public:

   …….

private:

   std::string mane;                                                    // 이 클래스를 이루는 객체 중 하나

   Address address;                                                  // 마찬가지

   PhoneNumber voiceNumber;                                    // 역시 마찬가지

   PhoneNumber faxNumber;                                       // 이것도 마찬가지

Person 객체는 string, Address와 PhoneNumber 객체로 이루어져있습니다. 이런 경우를 두고 합성이라는 말을 사용하는데 다른 말로는 레이어링(layering), 포함(containment), 통합(addregetion), 내장(embedding)이라고도 합니다.

 

항목 32에서 public 상속의 의미가 “is – a(..는 …의 일종이다)”라고 배웠습니다. 객체 합성 역시 의미를 갖고 있습니다. 실제 뜻이 2개나 되는데 2개인 이유는 소프트웨어 개발에서 대응하는 영역이 두가지이기 때문입니다. 사물을 본뜬 사람, 이동수단, 비디오 프레임 등인데, 이런 객체는 소프트웨어의 응용 영역(application domain)에 속하고, 나머지들은 버퍼, 뮤텍스, 탐색 트리 등 순수하게 시스템 구현만을 위한 인공물입니다. 이런 종류의 객체가 구현 영역(implementation domain)이라고 합니다. 여기서 객체 합성이 응용 영역의 객체들 사이에서 일어나면 has –a 관계입니다. 반면, 구현 영역에서 일어나면 is-implemented – in – terms – of 관계를 나타낸것입니다.

 

위의 person 클래스는 has –a 관계입니다. 사람이 이름의 일종, 사람이 주소의 일종이라고는 말할 수 없는것입니다. 그러나 사람이 이름을 가지며 사람이 주소를 가진다고 하는 것은 자연스럽습니다. 이렇기 때문에 is - a관계와 has - a관계의 역할을 헷갈리는 경우는 없습니다.

 

헷갈리는 부분은 is - a관계와 is – implemented – in – terms – of 관계의 차이점 일것입니다.

한 예로, 객체로 구성된 작은 집합, 중복 원소가 없는 집합체를 나타내고 저장 공간도 적게 차치하는 클래스 템플릿이 필요하다고 했을 때, 표준 라이브러리의 set템플릿을 활용해 보기로 합니다.

set템플릿은 원소 한 개당 포인터 세 개의 오버헤드가 걸리도록 구현되어 있습니다.

 

그러므로 표준 라이브러리의 set템플릿은 적당하지 않습니다. 이번엔 list를 사용해보겠습니다.

이번 포인트는 set템플릿을 만들되 list에서 파생된 형태부터 시작되도록 만든다는 것입니다.

set<T>는 list<T>로부터 상속을 받습니다.

template<typename T>                      // set을 만든답시고 list를 잘못 쓰는 방법

class set : public std::list<T> { … };

항목 32의 설명에 따르면 D와 B 사이에 is - a관계가 성립하면 B에서 참인 것들이 전부 D에서도 참이어야 합니다. 하지만 list 객체는 중복 원소를 가질수 있는 컨테이너 입니다. 구현하고 싶은건 중복된 원소가 있어서는 안되는 겁니다.

이들 두 클래스 사이의 관계가 is – a가 될 리 없으므로, public 상속은 지금의 관계를 모형화하는 데 맞지 않습니다.

 

해결법은 set객체는 list 객체를 써서 구현되는 (is implemented in terms of) 형태의 설계가 가능하다는 것입니다.

template<class T>              // set를 만드는 데 list를 제대로 쓰는 방법

class set {

public:

    bool member(const T& item) const;

    void insert(const T& item);

    void remove(const T& item);

    std::size_t size() const;

private:

    std::;ist<T> rep;                         // set 데이터의 내부 표현부

};

 

template<typename T>

bool set<T>::member(const T& item) const

{

             return set<T>::find(rep.begin(), rep.end(), item) != rep.end();

}

 

template<typename T>

void set<T>::insert(const T& item)

{

             If ( !member(item) )   rep.push_back(item);

}

 

template<typename T>

void set<T>::remove (const T& item)

{

             typename std::list<T>::iterator it = std::find(rep.begin(), rep.end(), item);

 

             If ( it != rep.end() )    rep.erase(it);

}

template<typename T>

std::size_t set<T>::size() const

{

             Return rep.size();

}

인라인 함수로 만들어도 될 정도의 구현입니다.

 

이 관계는 is – a가 아니라, is – implemented – in – terms – of 입니다.

 

이것만은 잊지 말자

-         객체 합성(composition)의 의미는 public 상속이 가진 의미와 완전히 다릅니다.

-         응용 영역에서 객체 합성의 의미는 has – a(…는 …를 가짐)입니다. 구현 영역에서는 is – implemented – in – terms – of(…는 ….를 써서 구현됨)의 의미를 갖습니다.

Posted by 최우림 -=HaeJuK=-
Study/Effective C++2010.03.08 10:26

C++에서 상속 받을수 있는 함수 : 가상, 비가상 함수

상속되는 함수는 동적으로 바인딩 된도, 기본 매개변수는 정적 바인딩된다.

가상함수는 동적으로 바인딩되지만, 런타임 효율성 때문에 기본 매개변수 값은 정적으로 바인딩된다.

동적 바인딩은 늦은 바인딩(late binding)이라고도 불리는데, 객체가 실행시 그 순간의 상태에 따라 일어난다. 프로그래밍에서 변수들은 정수나 문자열 등 형태에 따라 그 변수에 저장되는 값의 저장방법이나 조작방법이 달라진다. 전통적인 컴파일러들이나 어셈블러들은 컴파일 시점에 변수의 형식을 할당하는 정적 바인딩을 제공하였으나, 객체지향 언어들은 실행시 키보드 또는 기타 다른 소스로부터 값이 변수에 들어오는 그 순간에 변수의 형식이 결정되는 동적 바인딩을 지원한다. 이른 바인딩(early binding) 또는 정적 바인딩(static binding)과 대비되는 말이다.

//기하학 도형을 나타내는 클래스

Class Shape {

Public :

             Enum ShapeColor{ Red, Green, Blue };

//모든 도형은 자기 자신을 그리는 함수를 제공해야함

Virtual void draw(ShapeColor color = Red) const = 0;

};

 

Class Rectangle : public Shape {

Public :

             //기본 매개변수 값이 달라진 부분을 놓치지 마세요

             Virtual void draw(ShapeColor color=Green) const;

};

 

Class Circle : public Shape{

Public :

             Virtual void draw (ShapeColor color) const;

};

클래스 구조

                           Shape

 


Rectangle                          Circle

 

Shape *ps;                        //정적 타입 = Shape*

Shape *pc= new Circle;

Shape *pr= new Rectangle;

Ps, pc, pr, 은 모두 Shape 에 대한 포인터로 선언되어 각각의 정적 타입도 모두 Shape* 입니다

객체의 동적타입은 현재 그 객체가 진짜로 무엇이냐에 따라 결정되는 타입 입니다.

‘이 객체가 어떻게 동작할 것이냐’를 가리키는 타입이 동적 타입이라고 하겠습니다.

ps의 동적타입은 없고, Pc의 동적 타입은 Circle* , pr의 동적타입은 Rectangle *  입니다

 

동적타입은 프로그램 실행중에 바뀔수 있습니다.

Ps = pc;              // ps의 동적 타입은 Circle * 으로 바뀜

Ps= pr;                 // ps의 동적 타입은 Rectangle *으로 바뀜

가상함수는 동적으로 바인딩 됩니다(가상함수의) 호출이 일어난 객체의 동적 타입에 따라서 어떤 함수가 호출될지 결종된다는 뜻.

기본매개변수는 정적으로 바인딩 되기 때문에 매개변수 값이 설정된 가상함수는 뭔가 꼬이기 시작합니다. 파생 클래스에 정의된 가상 함수를 호출하면서 기본 클래스에 정의된 기본 매개변수 값을 사용해 버릴수도 있습니다.

 

Pr -> draw();        //Rectangle :: draw(Shape::Red) 호출

 

Pr의 동적타입은 Rectangle* 이므로 가상함수는 Rectangle 의 것이다

Rectangle::draw 함수의 초기값은 Green 이지만 pr의 정적타입이 Shape*이기 때문에 값을 Shape클래스에서 가져옴. 기본 매개변수 값들 중 하나가 파생클래스에서 재정의 되면 문제가 생김.

 

문제점이 발생되는 이유는 런타임 효율이라는 요소 때문입니다. 함수의 기본매개변수가 동적으로 바인딩된다면 프로그램 실행 중에 가상 함수의 기본 매개변수 값을 결정할 방법을 컴파일러 쪽에서 마련해 주어야할것입니다. 이방법은 컴파일 과정에서 결정하는 현재의 매커니즘보다 느리고 복잡할것이다.

이러한 이유로 지금 효율 좋은 실행 동작을 누릴수 있게 된것입니다

 

해결책
비가상 인터페이스(Non-Virtual Interface) 관용구(NVI 관용구)를 쓰는것. 이 방법은 파생 클래스에서 재정의할 수 있는 가상 함수를 private 멤버로 두고, 이 가상 함수를 호출하는 public 비가상 함수를 기본 클래스에 만들어 두는 것이다
비가상 함수가 기본 매개변수를 지정하도록 할수 있고, 이 비가상 함수의 내부에서 진짜 일을 맡은 가상함수를 호출하게됨.

Class Shape {

Public :

             Enum ShapeColor{ Red, Green, Blue };

void draw(ShapeColor color = Red) const ;                  //비가상함수

{

  doDraw(color);                                                       //가상함수 호출

}

Private:

             Virtual void doDraw(ShapeColor color) const =0; // 진짜 작업이 이루어지는 함수

};

 

 

Class Rectangle : public Shape {

Private:

             Virtual void doDraw(ShapeColor) const;          //기본 매개변수 값이 없음

};

이것만은 잊지 말자!

상복받은 기본 매개변수 값은 절대로 재정의해서는 안됩니다. 왜냐하면 기본 매개변수 값은 정적으로 바인딩되는 반면, 가상함수는 동적으로 바인딩되기 때문입니다

Posted by 최우림 -=HaeJuK=-

티스토리 툴바