728x90

#pragma 

Precomplier 즉 전처리기로서 컴파일 하기 이전에 해당 명령을 처리해주는 명령어이다.

Visual C++에는 다양한 프로젝트 옵션 등이 제공되지만

혹여 다른 컴파일러를 사용할 경우도 생길지 모르니

옵션 위주보다는 #pragma를 사용하는 것을 권장한다.

몇가지 옵션을 소개한다.

 

#pragma once : 해당 소스가 단 한번만 포함되게 한다.

 - 여러번 include 되는 것을 방지하며 이 파일은 한번만 포함되는 것을 알 수 있다.

   #ifndef #endif 와 같은 방식이다.

 

 

#pragma pack : 구조체의 정렬 방식을 지정한다.

 - 제어문 종류 :: pack(n) : n으로 정렬 방식을 바꾼다.

     default : Default 정렬 방식을 바꾼다.

     pack(push, n) : n으로 정렬 방식을 바꾸고 기존 정렬 방식은 스택에 저장한다.

     pop : 스택에 저장한 정렬방식으로 다시 돌린다.

     여기서 n은 컴파일러가 저장된 메모리에 데이터를 정렬하는 방법을 결정하는 바이트 크기이다.

     보통은 4바이트이다.

 

ex) #pragma pack(1)

typedef struct

{

     char ch1;      // 1바이트

     char ch2;      // 1바이트

     char* pchar; // 4바이트

     int a;            // 4바이트

     int* pa;         // 4바이트

}STUDENT, *LP_STUDENT;

#pragma pack() 이 경우 구조체의 크기는 14바이트여야 하나 실제로 sizeof()를 구해보면 16바이트이다. 이 때 pack을 사용하면 빈공간없이 14바이트로 구조체를 사용할 수 있다.

 

 

#pragma data_seg : dll에서 데이터를 공유하고자 할 때 사용된다.

 

 

#pragma warning : 컴파일시 나오는 경고를 설정한다.

 - 제어문 종류 : once : 번호 -> 반복되는 경고를 한번만 출력

      default : 번호 -> 원래 설정대로 되돌린다.

      disable : 번호 -> 경고 출력을 하지 않는다.

      error : 번호 -> 경고를 에러로 처리한다.

      level : 번호 -> 경고의 레벨을 변경한다.

      push[, n] -> 모든 경고의 레벨을 저장한다. n이 있을 경우 저장과 동시에 전역 경고 레벨을 n으로 변경한다.

      pop -> 스택에 마지막으로 저장된 경고 레벨을 복원한다.

 

ex) #pragma warning(disable : 4244)

 - 4244번의 Warning에 대한 경고를 표시하지 않는다.

    #pragma warning(3 : 4244)

 - 4244번 경고를 레벨 3으로 올린다.

 

 

#pragma message("Hello") : 컴파일 시 특정 문장을 표시하게 한다.

ex)

      #define __DISMSG__(x) #x

      #define __PREFIX__(x) __DISMSG__(x)

      #define __WARNING__ __FILE__ "("__PREFIX__(__LINE__)") warning : "

      #define __ERROR__  __FILE__ "("__PREFIX__(__LINE__)") error : "

 

      #pragma message(">> compliing begin point...")

 

      int main()

      {

           char szMsg[100];

           memset(szMsg, 0x00, sizeof(szMsg));

           strcpy(szMsg, "helloworld");

           #pragma message(">> "__WARNING__ "strcpy로 하면 안전하지 않다.")

           printf("%s\n", szMsg);

      }

 

      #pragma message(">> compliing end point...")

 


 

 

#pragma intrinsic(함수명1, 함수명2) : 해당 함수를 호출시 function call이 아닌 inline 형식으로 코드자체를 함수 사용하는 장소에 

                                           삽입시켜 최적화를 시켜준다.

 

 

#pragma function(함수명1, 함수명2) : 해당 함수를 function call 형태로 구현되도록 지시한다.

                                           둘중에 하나를 선언 후 다시 변경할 때까지 pragma가 기술된 첫 함수부터 영향을 미쳐 소스

                                           코드의 마지막 라인까지 영향을미친다.

 

 

#pragma comment(lib, "링크명.lib") : 해당 dll이나 라이브러리를 링커에 포함시키도록 한다.

 

 

#pragma optimize("옵션", on/off) : 함수 밖에 선언이 되며 pragma가 선언된 후 정의된 첫 번째 함수

                                                 에 영향을 미치도록 한다.

옵션 - g : 전역 최적화 옵션을 활성화

     - p : Floating Point의 Consistency를 향상시킨다.

     - s or t :

     - y : 프로그램 스택의 프레임 포인터를 일반화 시킨다.

     - 옵션의 아무것도 주지 않고 뒤에 파라미터를 사용시 모든 옵션에 대해 설정이 적용된다.

 

ex) #pragma optimize("", on)

    #pragma optimize("tp", off)

 

 

#pragma region & endregion : 멤버 함수나 변수들을 일정한 분류를 시켜준다. 해당 영역을 줄이거나

                                          확장시켜 코드의 가독성을 높일 수 있다.

 

ex) #pragma region 테스트용 함수들

void Test1() {}

void Test2() {}

void Test3() {}

  #pragma endregion 테스트용 함수들

 

더 자세한건 msdn을 찾아보자.

http://msdn.microsoft.com/ko-kr/library/h7k3a0bw.aspx

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

멤버 이니셜라이져(Member Initializer) 

 

말 그대로 클래스의 멤버를 초기화 하는 것으로

 

클래스

{

     멤버변수 1, 멤버변수 2;

}

클래스명(타입1 변수1, 타입2 변수2) : 멤버변수1(변수1), 멤버변수2(변수2){} 방식이다.

 

ex)

class Student

{

     int id;

     float num;

}

 

Student(int _id = 0, float ft_num = 0.0f) : id(_id), num(ft_num){}

 

이 문장들은 멤버변수 id와 num을 변수 _id 와 ft_num으로 초기화 하라는 말이다.

멤버 이니셜라이져는 생성자의 몸체부분보다 먼저 실행되는 특징이 있기 때문에 상속을 받은 클래스에서 부모 클래스의 변수를 초기화 해야 할 필요가 있을 경우 사용한다.

또한 const 멤버 변수를 초기화 할 수 있는 특징도 가지고 있기에 유용하게 쓰이는 문법이다.

그래서 const 멤버 변수는 반드시 이니셜 라이져를 이용해서 초기화 한다.

 

effective c++에서는 클래스의 변수를 초기화할 때에는 생성자의 함수 몸체에서 하지말고 멤버 이니셜라이져를 사용해라고 한다. 이유는 생성자의 멤버 이니셜라이져는 초기화이지만, 생성자의 함수 몸체에서 한다면 그것은 그냥 대입이라고 한다.

 

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

Zeromemory(&구조체, sizeof(구조체)); 

memset(&구조체, 0, sizeof(구조체));

 

이 두개는 컴파일 최적화 옵션을 해제하고 디스 어셈블 해보면

004017F0     push     24h

004017F2     push     0

004017F4     lea        eax, [ebp-70h]

004017F7     push     eax

004017F8     call       memset(401000h)

004017FD    add       esp, 0CH

 

결과 값과 컴파일 결과가 일치하는 것을 알 수 있다.

인자를 스택에 넣고, memset의 주소를 콜하는 방식이다.

실제적으로 두개의 차이는 없다고 할 수 있지만 Zeromemory는 0으로만 채워줄 뿐 memset과 같이 원하는 값을 초기화 값으로 줄 수 없다.

단지 Zeromemory는 0x00으로 해주는 수고를 줄여줄 뿐이다.

가독성면으로 보더라도 Zeromemory가 0으로 초기화 하겠다는 것을 쉽게 알 수 있으므로 별 차이가 없다면 Zeromemory를 쓰도록 하자.

 

또 다른 구조체 초기화 방법은

구조체타입 객체변수 = {0};

ex) Student S1 = {0};

 

코드를 디스 어셈블 해보면 결과는

00401800     mov     dword ptr[ebp-4Ch], 0

00401807     xor      ecx, ecx

00401809     mov     dword ptr[ebp-48h], ecx

0040180C    mov     dword ptr[ebp-44h], ecx

0040180F    mov     dword ptr[ebp-40h], ecx

 

2번째 줄에서 볼 수 있듯이 xor, ecx, ecx 명령어로 단순히 그 값을 0으로 채워주는 것 밖에 없다.

한가지 알아 둘 것은 구조체의 크기가 커지면 컴파일 시 자동으로 memset으로 변환한다는 것이다. 

 

즉  3개의 차이는 그다지 크지 않으므로 자신이 보기 편하고 가독성이 좋다고 생각하는 것을 쓰도록 하자.

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

volatile 키워드 

 

컴파일러의 최적화 옵션의 적용을 받지 않도록 하는 선언자.

비슷한 것으로 constant 선언이 있는데 서로 다른 점이라면  constant 선언의 경우에는 변수 정의 시점에 대입된 값이 어떠한 경우에도 변경되지 않도록 보호하는 형태 즉 상수의 형태로 보존되도록 하라는 옵션이다.

 

이와 달리 volatile의 경우에는 컴파일러가 프로그램의 코딩을 최적화를 통해 임의로 수정하지 못하도록 하는 명령어로 변수 선언시 임의로 레지스터에 변수를 올리지 않게 하고 메모리에 올리게 하며 코드는 임의로 수정을 하지 못하게 한다.

 

즉, 해당 변수의 데이터가 변할 수 있기 때문에 항상 해당 메모리에 접근해서 데이터를 직접 참조하라는 지시자이다.

 

간단한 예)

int i, a, b;

 

a = 0; b = 0;

 

for(i = 0; i < 10; i++)

     b += a * 100;      

 

이런 코드를 작성한 경우.. 일반적인 컴파일러로 최적화를 하게 되면

 

int i, a, b;

 

a = 0; b = 0;

 

for(i = 0; i < 10; i++)

     b += 0;       // 연산을 하지 않게 컴파일러가 알아서 보정해 버린다.

                         (최적화 옵션에 따라 다양하게 변할 수 있다.)

 

이처럼 컴파일러 차원에서 옵티마이저시 위와 비슷한 로직으로 자동 변환이 되게 되는데(정확히는 어셈블 과정에서 코드가 변환되어 비 효율적이라 판단되는 부분을 수정 또는 제거해 버린다.)

프로그램에 따라서는 이렇게 코드가 변경되면 안되는 경우가 있다.

즉 컴파일을 할 때 최적화 옵션으로 인해 변경될 경우를 방지하기 위한, 반드시 내가 명시한대로 실행할 필요가 있을 경우 사용하는 키워드이다.

 

또한 변수에 값을 대입 할 때 (메모리에 write) 항상 공유 메모리에 직접 쓰거나 읽지 않고 쓰기 버퍼(저장 버퍼)에 값을 임시로 저장했다가 실제 공유메모리에 write할 수 있다. 이럴 경우 멀티 스레드 환경에서 메모리를 읽었을 때 기대하는 값이 나오지 않을 수 있다.

 

 

register 키워드

 

해당 변수를 메모리에 저장하는 것이 아닌 실제로 연산이 일어나는 레지스터에 저장되게 한다.

레지스터는 고속 연산이 가능하므로 상당한 실행 시간을 보장 받을 수 있지만

용량의 한계가 있으므로 간단한 프로그램 위주로 많이 쓰인다.

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

1. static_cast 

묵시적 캐스르로 변수형을 변환해준다.

 

간단한 예)

int d = 65;

char ch = d;                              // 컴파일러가 묵시적으로 캐스팅을 해줌

char ch = static_cast<char>(d);  // 명시적으로 캐스팅을 해줌

  

묵시적 캐스팅과의 차이점

 - 클래스 포인터에 대해 묵시적 캐스트는 is-a관계가 성립하는 경우만 허용하고 static_cast는 is-a관

   계 뿐만 아니라 상속 관계일때도 캐스팅을 해준다.

 

Truck* pTruck = new Car;

Truck* pTruck = static_cast<Truck*>(new Car);

명시적 변환이기는 하나 컴파일 타임에 타입 체크를 하는 등 대체적으로 안전한 형 변환이다.

 

 

2. reinterpret_cast

일반적으로 허용하지 않는 위험한 형변환을 할 때 사용한다.

단지 안에 있는 비트열만 보고 원하는 형으로 강제 변환을 할 때 사용한다.

포인터를 정수로 변환하는 작업을 사용할 때 많이 사용한다.

 

간단한 예)

int a, b;

a = reinterpret_cast<int>(&b);

 

간단한 예2)

class A

{

private :

     char a;

     char b;

}

위 클래스 변수 b에 억지로 값을 기록하고자 한다면 다음과 같이 사용하면 된다.

A a;

char* p = reinterpret_cast<char*>(&a);

*(p+1) = 5;

 

 

3. dynamic_cast

유일하게 c스타일의 형변환으로 불가능한 캐스팅.

상속관계에 있는 클래스의 형변환을 수행하며, 동시에 안전한지 검사 한다.

 

간단한 예)

Truck* pTruck = new Car;

Truck* pTruck = dynamic_cast<Truck*>(new Car);

 

 

4. const_cast

const 속성이나 volatile속성을 해제 할 때 사용한다.

변수를 정의할 때 volatile 키워드를 사용한다.

즉 상수로 정의한 변수를 변경하고 싶을 때 사용한다.

 

간단한 예)

RECT rectA;

CONST RECT rectB;

 

rectA = const_cast<RECT>(rectB);

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

CUDA Stream은 어플리케이션을 가속시키는 데 중요한 역할을 한다. 하나의 CUDA Stream은 하나의 큐를 나타내며, 여기에는 특정 순서에 따라 실행될 GPU 작업들이 추가되어 있다. 커널의 실행, 메모리 복사 그리고 이벤트의 시작과 중지와 같은 작업들을 하나의 스트림에 추가할 수 있다. 스트림에 추가된 작업들의 순서가 곧 실행될 순서를 의미한다. 각 스트림을 GPU 상의 하나의 태스크로 여길 수 있으며, 이러한 태스크들은 병렬로 실행될 수 있다. 


CUDA Stream을 사용하기 위해서는 디바이스 오버랩(Device Overlap)이라고 알려진 기능을 제공하는지 확인해야한다.


CUDA Stream을 사용하는 방법은 다음과 같다.

CUDA Stream은 정 메모리를 이용해야만 한다.



728x90

'Parallel Programming > CUDA' 카테고리의 다른 글

CUDA - GPU Memory Usage Check  (0) 2016.11.22
CUDA - 고정 메모리(cudaHostAlloc())  (0) 2016.03.03
CUDA - 원자적 연산  (0) 2016.03.03
CUDA - 시간 측정  (0) 2016.02.26
CUDA - 2차원 배열의 할당과 이용  (0) 2016.02.25
728x90

CUDA 런타임은 cudaHostAlloc()이라는 CUDA만의 호스트 메모리를 할당하는 방법을 제공하고 있다.


사실 malloc()과 cudaHostAlloc()으로 메모리를 할당하는 것에는 중요한 차이점이 존재한다. C 라이브러리 함수인 malloc()은 표준의 페이징(pageable)이 가능한 호스트 메모리를 할당하는 반면, cudaHostAlloc()은 잠긴 페이지의 호스트 메모리를 할당한다.


때때로 고정 메모리(pinned memory)라고 불리는 잠긴 페이지의 메모리 버퍼들은 하나의 중요한 속성을 가지고 있다. 이는 운영 체제가 해당 메모리를 절대로 디스크로 페이징하여 내보내지 않는 것을 보장하며 실제 물리 메모리에 존재하도록 해준다.


버퍼의 물리적인 주소를 알게됨으로써 GPU는 호스트로부터 또는 호스트로 데이터를 복사하기 위해 직접 메모리 접근(DMA) 방식을 이용할 수 있다. DMA 방식의 복사는 CPU의 개입 없이 진행되므로 복사 도중 CPU가 버퍼를 디스크로 페이징하여 내보내거나 운영체제의 페이지 테이블을 갱신하면서 버퍼의 물리적인 주소를 재위치시킬 수 있음을 의미하기도 한다.

CPU는 페이징이 가능한 메모리의 데이터를 이동시킬 수 있으므로 DMA 방식의 복사를 위해서는 고정 메모리를 사용하는 것이 필수다. 


하지만 주의 할 점이 있다.


고정 메모리를 사용하는 것은 양날의 검이다. 고정 메모리를 사용하면 가상 메모리의 멋진 특징들을 효과적으로 이용하지 못한다. 구체적으로 말하면, 잠긴 페이지의 버퍼들은 디스크로 스왑(swap)될 수 없기 때문에 어플리케이션을 구동중인 컴퓨터는 잠긴 페이지의 모든 버퍼들에 대해 가용한 물리적인 메모리를 가져야만 한다. 이것은 시슽렘에서 표준 malloc()를 사용할 때보다 메모리 부족현상이 훨씬 빨리 발생할 수 있음을 의미한다. 이것은 어플리케이션이 더 적은 양의 물리적인 메모리로 인해 실패할 수 있음을 의미하며, 시스템에서 구동중인 다른 어플리케이션들의 성능에도 영향을 미칠 수 있음을 의미한다.





728x90

'Parallel Programming > CUDA' 카테고리의 다른 글

CUDA - GPU Memory Usage Check  (0) 2016.11.22
CUDA - 스트림(stream)  (0) 2016.03.03
CUDA - 원자적 연산  (0) 2016.03.03
CUDA - 시간 측정  (0) 2016.02.26
CUDA - 2차원 배열의 할당과 이용  (0) 2016.02.25
728x90

업무용 컴퓨터에 얼마 전부터 (글 작성시점은 2016년 3월) 


"컴퓨터에 api-ms-win-crt-runtime-l1-1-0.dll이(가) 없어 프로그램을 시작할 수 없습니다. 프로그램을 다시 설치하여 이 문제를 해결하십시오."


api-ms-win-crt-runtime-l1-1-0.dll is missing <-- 구글링한 문장은 이것.


란 메시지 박스가, 일부 프로그램을 실행할 때마다 떴다. 메시지 박스의 "확인"버튼만 눌러주면 프로그램은 정상적으로 실행되는 것 같았지만, 꺼림직하여 구글링으로 문제의 해결방법을 찾았다.


문제는 visual studio 2015용 visual c++ 재배포 패키지를 마이크로소프트에서 다운받아 설치하여 해결됐다. 32비트 재배포 패키지와 64비트 재배포 패키지를 받을 수 있는데, 나의 경우는 64비트 재배포 패키지만 받아서 깔아 해결됐다.


문제가 발생한 환경은 한국어 윈도우7 64비트.


출처 : http://daewonyoon.tistory.com/179



728x90

+ Recent posts