Windows Vista 부터 새로 생긴 API CompareStringEx 라는 것이 있습니다.

함수는 이름을 보면 알듯이 문자열을 비교하는 것인데 일반적인 문자열 비교함수와 조금 다른 점 있습니다.

 

일반적인 문자열 비교에서는 문자열 "10" 문자열 "3" 비교하면 문자열 "3" 것으로 나옵니다.

그러나 보통 문자열을 비교한다면 숫자 비교처럼 결과가 나오기 바라는 경우가 많을 입니다.

이때 CompareStringEx 사용하면 좋습니다.

 

바로 예제 들어갑니다.^^

#include <windows.h>

#include <iostream>

 

int main()

{

    setlocale(LC_ALL, "Korean");

 

    wchar_t strResult[128] = {0,};

    wchar_t CSTR_RET[4][32] = { L"에러", L"작다", L"같다", L"크다" };

    int iCompareResult = 0;

    

    // 숫자 비교

    wchar_t szNumString10[16]    = L"10";

    wchar_t szNumString3[16]    = L"3";

 

    iCompareResult = CompareStringEx(LOCALE_NAME_SYSTEM_DEFAULT, SORT_DIGITSASNUMBERS,

                        szNumString10, -1, szNumString3, -1, NULL, NULL, 0);

    wsprintf(strResult, L"CompareStringEx %s는 %s보다 %s", szNumString10, szNumString3, CSTR_RET[iCompareResult] );

 

    std::wcout << strResult << std::endl << std::endl;

    

 

    iCompareResult = CompareStringEx(LOCALE_NAME_SYSTEM_DEFAULT, 0,

                        szNumString10, -1, szNumString3, -1, NULL, NULL, 0);

    wsprintf(strResult, L"CompareStringEx %s는 %s보다 %s", szNumString10, szNumString3, CSTR_RET[iCompareResult] );

 

    std::wcout << strResult << std::endl;

 

    return 0;

}

 

  < 결과 >

 

결과를 보면 알 수 있듯이 CompareStringEx 사용할 번째 인수로 비교 플래그 값을 설정할 SORT_DIGITSASNUMBERS 을 사용하면 문자열 비교를 숫자의 크기로 비교해 줍니다.^^

 

CompareStringEx

MSDN : http://msdn.microsoft.com/en-us/library/dd317761%28VS.85%29.aspx

신고
by 흥배 2012.10.18 09:00
Windows7은 사용자뿐만이 아닌 개발자에게도 편리한 기능을 가지고 있습니다.
그 중에 '대기 체인 분석' 이라는 것이 있습니다.
이것을 사용하면 프로그램을 종료 시켰는데는 프로세스는 죽지 않고 살아 있는 경우 어디서 문제가 되었는지 어느 정도 알아낼 수 있습니다.


이 프로그램은 제가 집에서 만든 서버 애플리케이션으로 X 버튼을 눌러서 종료시킵니다.


그런데 작업 관리자를 보면 프로세스가 아직 죽지 않았습니다(ProjectHF2.exe)

이 문제는 보통 워커 스레드를 사용하는 경우 특정 스레드가 죽지 않아서 발생하는 경우일것입니다.
그럼 어떤 스레드가 대기 상태에 있는지 알기 위해서 대기 체인 분석을 사용해보겠습니다.


리소스 버튼을 누르면 아래의 창이 나옵니다.


위 그림과 같이 프로세스를 선택 후 메뉴에서 '대기 체인 분석'을 선택합니다.


위와 같은 화면이 나오고 어떤 스레드가 어떤 이유로 대기하고 있는지 설명해줍니다.

이 기능은 Windows API 중 Wait Chain Traversal를 사용한 것입니다.
http://msdn.microsoft.com/ko-KR/library/ms681622(VS.85).aspx

어느 정도 제한은 있지만 이 API를 사용하면 데드락 상황도 판단할 수 있습니다.














저작자 표시
신고
by 흥배 2011.01.14 09:00
시스템 에러가 발생해서 GetLastError()을 사용해서 에러 값을 보면 숫자이기 때문에 어떤 에러인지 알기 힘듭니다. 이때 Win32 API인 FormatMessage를 사용하면 에러 문자열로 변환해주므로 에러 내용을 알기 편합니다.


제 경우는 네트웍에서 에러가 발생하면 아래와 같이해서 로그로 남기도록 합니다.

void Socket::SocketError( INT32 ErrCode, char* pFunctionName )
{
   // pFunctionName는 이 에러가 발생한 곳의 함수 이름
    LPVOID lpMsgBuf;
   
    FormatMessage
        (   FORMAT_MESSAGE_ALLOCATE_BUFFER
          | FORMAT_MESSAGE_FROM_SYSTEM
          | FORMAT_MESSAGE_IGNORE_INSERTS
        , NULL
        , ErrCode
        , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
        , (LPTSTR) &lpMsgBuf
        , 0
        , NULL );

    Logger->Write( "%s - GetLastError(%d - %s)", pFunctionName, ErrCode, lpMsgBuf );
}



이 API를 사용하기 위한 조건
OS : Windows 2000 이상
헤더파일 : winbase.h(windows.h에 포함되어 있음)
라이브러리 : kernel32.lib
DLL : kernel32.dll


자세한 내용은 아래를 참고해 주세요

MSDN : http://msdn.microsoft.com/en-us/library/ms679351%28VS.85%29.aspx



저작자 표시
신고
by 흥배 2010.12.06 09:00

CryptGenRandom는 랜덤 값을 생성하는 함수로 C 라이브러리의 rand와 비슷합니다.

틀린 점은 rand는 랜덤 값이 예측 가능하고 CryptGenRandom는 암호화 기능을 사용하여 예측 할 수 없는 랜덤 값을 생성해 줍니다.
(rand
의 경우 srand을 사용한 후 사용해야 이전과 중복되지 않은 패턴으로 랜덤 값을 생성합니다)

 


CryptGenRandom의 또 하나의 장점은 시드 값의 자료형 크기를 넘지 않는 랜덤 값을 생성합니다.
즉 시드 값의 자료형이 unsigned char라면 CryptGenRandom을 사용한 랜덤 값은 0 ~ 255 사이의 값만 나옵니다.


CryptGenRandom는 기존의 srand+rand의 조합에 비해서 장점이 있지만 단점도 있습니다. 단점은 성능입니다. CryptGenRandom가 암호화 기능을 사용하므로 rand에 비해서 느립니다. Win32 API에서는 어느 정도 차이가 나는지 잘 모르겠지만 똑 같은 기능이 있는 닷넷에서는 rnadCryptGenRandom의 성능 차이는 대략 100배 정도라고 합니다.

 

 

 

참고

Step By Step: Visual C++에서 난수 생성 방법
http://support.microsoft.com/kb/983137/ko

 

[MSDN] CryptoRandom 이야기
http://msdn.microsoft.com/ko-kr/magazine/cc163367.aspx

 

저작자 표시
신고
by 흥배 2010.11.08 09:00

시간 계산 어떻게 하시나요?

여러 가지 방법이 있겠지만 SYSTEMTIME을 사용하여 계산하는 경우라면 COleDateTimeCOleDateTimeSpan을 사용하여 계산하면 쉽게 할 수 있습니다.

COleDateTimeCOleDateTimeSpan ATL에 있는 라이브러리입니다.

 

 

COleDateTimeCOleDateTimeSpan을 사용하기 위한 조건

필요한 파일 : ATLComTime.h

 

 

예제

아래 예제는 지정된 시간에서 을 더한 시간을 계산합니다.

void UpdateTime( SYSTEMTIME& stPatchTime, INT32 wMinute )

{

COleDateTime PatchTime( stPatchTime.wYear, stPatchTime.wMonth, stPatchTime.wDay, stPatchTime.wHour, stPatchTime.wMinute, stPatchTime.wSecond );

 

COleDateTimeSpan SpendTime;

SpendTime.SetDateTimeSpan( 0, 0, wMinute, 0 );

PatchTime += SpendTime;

          

stPatchTime.wYear = PatchTime.GetYear();

stPatchTime.wMonth = PatchTime.GetMonth();

stPatchTime.wDay = PatchTime.GetDay();

stPatchTime.wHour = PatchTime.GetHour();

stPatchTime.wMinute = PatchTime.GetMinute();

}


 

COleDateTime은 생성자에서 SYSTEMTIME을 인자로 사용할 수 있습니다. 그러나 인자로 사용할 SYSTEMTIME의 값이 올바른 값인 경우만 사용할 수 있습니다. 예를 들어 아래와 같은 것을 인자로 넘기면 디버그 모드에서는 경고가 발생합니다.

SYSTEMTIME stTime;

stTime.wYear = 2010;

stTime.wMonth = 10;

stTime.wDay = 11;

 

COleDateTime CurTime( stTime );

// 이후 이것을 계산에 사용할 때 에러발생

 

 

저작자 표시
신고
by 흥배 2010.10.15 09:00

프로그램에서 현재 실행 중이 컴퓨터의 CPU에 대한 정보를 알아야 할 때가 있습니다.


예를들면

1. CPU 32비트인지 64비트인지

2. 코어가 몇 개 있는지

등등...


이런 정보를 알고 싶을 때는 Windows 2000 때까지는 GetSystemInfo를 사용했습니다.

그러나 GetSystemInfo 64비트 Windows에서 이 API를 사용하는 프로그램은 32비트 프로그램이라면 잘못된 정보를 가져옵니다. 그래서 32비트나 64비트 환경에 상관 없이 정확한 정보를 얻기 위해서는 GetNativeSystemInfo를 사용해야 합니다.

GetNativeSystemInfo 64비트 환경에서는 내부적으로 GetSystemInfo를 호출합니다.

 

 

 

 

GetNativeSystemInfo를 사용하기 위한 조건

 

OS : Windows XP 이상

필요한 파일 : windows.h

필요한 라이브러리 : kernel32.lib

필요한 dll : kernel32.dll

 

API를 사용할 때 헤더파일에 _WIN32_WINNT 선언이 0x0501 이상이어야 합니다.

#define _WIN32_WINNT 0x0501

 

 

예제

#include <iostream>

#include <windows.h>

 

int main()

{

           SYSTEM_INFO sInfo;

           GetNativeSystemInfo(&sInfo);

                    

           std::cout << "CPU 개수 : " << sInfo.dwNumberOfProcessors << std::endl;

           std::cout << "프로세스 아키텍처 : " << sInfo.wProcessorArchitecture << std::endl;

          

           getchar();

           return 0;

}


결과


저작자 표시
신고
by 흥배 2010.10.06 09:00

QueryPerformanceCounter가 나오기 전에는

 

x86 P5 명령 셋 도입 이후 높은 정밀도의 시간을 얻기 위해서 RDTSC(Read Time Stamp Counter)를 사용하였습니다. 그러나 RDTSC는 아래와 같은 문제가 있었습니다.

 

1. 연속성 없는 값. 멀티 코어, 멀티 프로세스에서는 코어간의 사이클 카운터가 동기화 되지 않습니다.

2. 전용 하드웨어의 가능성. RDTSC가 나온 이후 시간이 지나면서 마더보드에서 높은 정밀도의 시간 정보를 제공하는 디바이스를 제공하고 있습니다.

3. CPU 주파수의 변동성. 전원관리기술에 의해서 CPU 주파수가 변동됩니다.

 

위와 같은 문제를 안고 있는 RDTSC를 대체하기 위해 Win API에서는 QueryPerformanceCounter를 제공합니다.

 

 

 

그런데 QueryPerformanceCounter는 멀티 코어, 멀티 CPU에서 문제가 없을까요?

 

아니요 있습니다. RDTSC 1번 문제처럼 이것도 연속성 없는 값문제를 해결하지 못하고 있습니다. QueryPerformanceCounter를 문제 없이 사용하기 위해서는 이것을 하나의 스레드에서 사용하고 이 스레드를 하나의 프로세서에 고정시켜야 합니다.

 

원래 이 API 자체가 이런 멀티 코어, 멀티 CPU를 생각하지 않고 나온 것이 아닙니다. 오히려 이런 문제를 해결하는 것이 목적 중의 하나입니다. 그런데 왜 특정 프로세서에 고정시켜서 사용해야 할까요? 이유는 두 가지가 있습니다.

 

1. 하드웨어 버그

이것은 마이크로 소프트의 문제가 아니고 하드웨어 업체의 문제입니다. BIOS나 드라이버에 버그가 있으면 스레드가 프로세서 간 이동할 때 이들 루틴에서 서로 다른 값을 넘기는 경우가 있어서 이런 하드웨어 버그를 피하기 위해서 특정 프로세서에 고정시키는 것이(SetThreadAffinityMask 사용) 좋습니다.  

 

2. 병목

각 워커 스레드에서 시간 계산을 하면 각 스레드로부터 동기화 하기 위해 락을 걸어야 하므로 이것에 의한 병목이 발생합니다. 이것을 막기 위해서 하나의 스레드에서 시간 계산을 하도록 하여 락을 사용하지 않도록 합니다.

 

 

 

그럼 QueryPerformanceCounter를 하나의 스레드에서만 사용하고 이 스레드를 특정 프로세서에 고정시키는 것으로 문제는 없을까요?

 

아닙니다. 문제(?)는 여전히 있습니다. 이 문제는 근래에 생긴 문제로 원인 제공은 인텔입니다. 바로 인텔의 네할렘 계열 CPU가 문제가 됩니다. 네할렘 계열 CPU로는 i3, i5, i7이 있는데 이들 CPU터보 부스터기능이 있습니다. 이 기능은 각 코어의 주파수는 2.6인데 특정 코어가 부하가 걸리고 나머지 코어가 여유가 있으면 부하가 걸린 코어의 주파수를 올려줍니다(즉 2.6이 2.8이 되어버립니다).

 

주파수를 올려준다이 말에서 문제를 아시겠죠? OS에서는 시스템이 시작할 때 CPU의 주파수를 설정한 후 이후 다시 시스템을 재 시작 하기 전까지는 변경하지 않습니다. 그리고 QueryPerformanceCounter OS가 설정한 CPU 주파수를 사용합니다. 그런데 터보 부스터기능에 의해서 실제 주파수는 변경되어 버립니다. 그래서 터보 부스터가 작동하면 QueryPerformanceCounter의 값은 작동하기 전과 다른 값이 나와버립니다.

 

그래서 성능 측정에서 QueryPerformanceCounter을 사용할 때는 터보 부스터기능을 off로 해 놓아야 합니다. 그리고 게임과 같은 애플리케이션에서 사용할 때는 터보 부스터에 의해서 오차가 생길 수 있음을 유의해야 합니다.

 

 

 

터보 부스터QueryPerformanceCounter 문제 해결 방법은?

 

이건 저도 모릅니다.^^;

다만 Windows 7에서는 이런 문제가 없다고 합니다. 이유는 Windows 7는 이전 OS와 다르게 QueryPerformanceCounter에 다른 레퍼런스 클럭을 사용하고 있기 때문에 CPU 주파수가 변동 되어도 영향을 받지 않는다고 합니다. 참고로 이런 이유로 Windows 7QueryPerformanceCounter의 부하가 XP 보다 조금 높다고 합니다.

 

 

 

 

참고

http://msdn.microsoft.com/ja-jp/library/bb173458%28VS.85%29.aspx

http://d.hatena.ne.jp/shiku_otomiya/20100218/p1

http://d.hatena.ne.jp/shiku_otomiya/20100504/p1

 

저작자 표시
신고
by 흥배 2010.06.07 08:30

근래에 회사에서 만들고 있는 게임의 서버 애플리케이션에 LFH을 적용하면서 관련 자료를 좀 모아 보았습니다.


LFH ( Low fragmentation Heap )

1. 단편화 방지를 위해서 만들어진 것이지만 멀티코어 환경에서 특히 좋다.

2. 코어가 하나 있을 때는 오히려 10% 성능 하락도 있다( 신뢰 여부는 측정 불가 ^^; )

3. CRT에서는 크게 성능 향상을 볼 수 없다.
   (nhn Japan 블로그 글에서 CRT도 LFH를 적용했는지 정확하게 모르겠음
    CRT도 LFH 적용을 위해서는 CRT 힙 핸들을 HeapSetInformation에 적용해야 한다 )

4. LFH가 극상의 성능을 내기 위해서 Private Heap과의 조합이 최상이다.

5. Windows Server 2008/Vista 기본적으로 사용되도록 되어 있음
   : 이 때문에 메모리 측정(Private Bytes)를 할 때 정보가 좀 틀릴 수도 있음

6. LFH는 Windows XP, Windows 2000 Professional with SP4, Windows Server 2003 이상 가능



참고

1. 책
  Windows via C/C++ 672 페이지

2. 웹
http://workspace.office.live.com/?wa=wsignin1.0#jacking75/프로그래밍%20문서/CHB001%20-%20About%20Heap.docx

http://kslive.tistory.com/49

http://www.cyworld.com/jupihistory/2870669

http://blog.naver.com/mmouse77?Redirect=Log&logNo=50014020705

http://blog.naver.com/neuk?Redirect=Log&logNo=150012867627

http://re-thinkthings.spaces.live.com/Blog/cns!1C5AE98DA3645F42!587.entry   (일본어 )

윈도우즈의 LFH와 Hoard의 비교
http://devnote.net/55


NHN Japan 블로그에 있는 글 ( 성능 측정 그래프도 있음 )
http://techlog.nhncorp.jp/index.php/low-fragmentation-heap/#more-21
  (일본어)




사용 방법

1. 프로세스 힙에 LFH 적용
ULONG ulEnableLFH = 2;
HeapSetInformation (
        GetProcessHeap(), 
        HeapCompatibilityInformation, 
        &ulEnableLFH, 
        sizeof(ulEnableLFH));

2. CRT 힙에 LFH 적용
intptr_t hCrtHeap = _get_heap_handle();
ULONG ulEnableLFH = 2;
if (HeapSetInformation((PVOID)hCrtHeap,
                           HeapCompatibilityInformation,
                           &ulEnableLFH, sizeof(ulEnableLFH)))
        puts("Enabling Low Fragmentation Heap succeeded");
    else
        puts("Enabling Low Fragmentation Heap failed");
    return 0;


3. 프로세스의 모든 힙에 적용

HANDLE Heaps[1025];
DWORD Count = GetProcessHeaps( 1024, Heaps );
for( DWORD i = 0; i < Count; ++i )
{
     ULONG  HeapFragValue = 2;

     if( HeapSetInformation( Heaps[i], HeapCompatibilityInformation, &HeapFragValue, sizeof(HeapFragValue) ) )
    {
          .............
    }
    else
    {
         ...................
    }
}


제가 적용한 프로젝트에서는 프로세스에서 생성한 힙이 총 9개이며 세번째는 무조건 실패를 합니다.

이유는 이 힙에는 HEAP_NO_SERIALIZE flag가 설정 되어 있기 때문입니다.

HEAP_NO_SERIALIZE flag는 멀티 스레드에서 그 힙에 동시에 접근하여 할당, 해제를 가능하게 하기 위한 설정입니다.
( 참고   http://support.microsoft.com/kb/929136/ja  )


모든 힙에 적용하므로 따로 CRT 힙에는 LFH를 적용하지 않아도 된다.   

모든 프로세스의 힙에 LFH를 적용 후 따로 CRT 힙에 LFH를 적용하면 실패합니다.

이유는 이미 적용을 했기 때문이다. 저는 9번째가 CRT 힙이었습니다.




Windows Heap과 관련된 볼만한 글입니다.

Analyzing the heap(s) of a Win32 program



 

이 글은 스프링노트에서 작성되었습니다.

신고
by 흥배 2009.05.21 22:00
| 1 |