2010년 6월 26일 토요일

notepad2 컴파일 삽질기 부록#6.1 : 메모장 대신 사용(수정)



okto님 배포본 notepad2는 설치 및 윈도우 메모장 대체를 위해 notepad.inf 파일을 이용했다.
이 방식은 설치가 쉽다는 장점은 있지만, 설치 제거에 손이 많이 간다는 단점이 있다.

장점을 살리고 단점을 제거하기 위해 구차니님께서 메모장을 대체하는 기능을 구현해주셨다.

그런데, 이를 위해 제작/공개한 notepad2 컴파일 삽질기 부록#6 : 메모장 대신 사용에는 사소한 버그가 하나 있었다.
바로 파일명을 notepad.exe로 바꾼 상태에선 동작하지 않는 다는 것이다.

이 부분은 사실 당연한 것이다.
이 기능은 레지스트리의 Image File Execution Options 키를 이용해서 notepad.exe라는 프로그램을 실행하면 다른 프로그램(notepad2.exe)을 실행하도록 지정하는 것인데, 그 프로그램이 또 notepad.exe라서 발생하는 문제이다.

이 기능을 사용하려면 소스 코드를 아래와 같이 수정하면 된다.

반드시 삽질기 부록#5.1 : context-menu 통합 및 수정을 적용한 후에 이 수정을 해야 함

수정 대상 파일은 역시 resource.h, Notepad2.rc, Notepad2.c 세 개이다.


1. resource.h

다음 줄을 추가한다.
#define IDM_REPLACE_NOTEPAD             40692



2. Notepad2.rc

아래 내용을 찾는다.
MENUITEM "Add context-menu to all file type", IDM_REGISTRY_ALL
, CHECKED
바로 앞에 다음 줄을 추가한다.
MENUITEM "Replace Notepad",             IDM_REPLACE_NOTEPAD



3. Notepad2.c

앞부분에 다음 변수를 하나 선언한다.
BOOL      bReplaceNotepad;


다음, void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
CheckCmd(hmenu,IDM_REGISTRY_UNKNOWN,bRegistryUnknownType);
또는
CheckCmd(hmenu,IDM_REGISTRY_ALL,bRegistryAllType || bRegistryUnknownType);
바로 앞에 다음 줄을 추가한다.
CheckCmd(hmenu,IDM_REPLACE_NOTEPAD,bReplaceNotepad);


다음, LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam) 함수에 다음 case 문을 추가한다.
이 함수 전체가 하나의 switch-case 문으로 되어있는데, case를 하나 추가하는 것이다.
case IDM_REPLACE_NOTEPAD:
if (bReplaceNotepad)
{
//WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe");
bReplaceNotepad = FALSE;
}
else
{
WCHAR path[MAX_PATH], *wTemp;
GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
bReplaceNotepad = TRUE;

wTemp = path;
while (*wTemp) wTemp++;
if (_wcsicmp(wTemp-12, L"\\notepad.exe"))
{
HKEY key1;
LONG res = RegCreateKey(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe",&key1);
if (res == ERROR_SUCCESS)
{
WCHAR cmd[MAX_PATH + 4];
int len = wsprintf(cmd, L"\"%s\" /z", path);
RegSetValueExW(key1, L"Debugger", 0, REG_SZ, (const unsigned char*)cmd, len * 2);
}
else
{
bReplaceNotepad = FALSE;
MessageBeep(MB_ICONEXCLAMATION);
}
RegCloseKey(key1);
}
else
{
bReplaceNotepad = FALSE;
MessageBeep(MB_ICONEXCLAMATION);
}
}
break;


마지막으로 void CheckRegistry() 함수의 마지막 부분에 다음 내용을 추가하면 된다.
res = RegOpenKey(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe",&key);
if(res == ERROR_SUCCESS)
        bReplaceNotepad = TRUE;
else    bReplaceNotepad = FALSE;
RegCloseKey(key);


수정된 결과는 아래와 같다. 짜잔~

사용자 삽입 이미지

수정에 수고해주신 구차니님께 다시 한 번 감사드립니다.

notepad2 컴파일 삽질기 부록#5.2 : context-menu 기능 수정

삽질기 부록#5.1 : context-menu 통합 및 수정을 수정한 포스팅. 부록 #5/#5.1과 본 포스트는 동시 적용불가.

구차니님께서 수고해주신 덕분에 context 메뉴(오른쪽 버튼 클릭 메뉴)를 통해 Notepad2를 사용할 수 있었다.
(삽질기 부록#1 : context-menu 추가삽질기 부록#2 : context-menu 추가 업그레이드 참조)

그런데, Notepad2 3.1.21 정식버전에서는 이 수정을 미묘하게 바꿔야 했으며, 이후 okto님구차니님과 context 메뉴에 대한 수정을 논의한 결과 메뉴를 조금 간결하게 변형하기로 했다.
(File 메뉴에 있는 context-menu 관련 메뉴를 하나만 남기도록 수정)

그리고, 레지스트리에 등록이 실패했을 때의 코드가 빠져있었는데, 실패했을 경우 간단한 경보음을 들려주도록 했다.

삽질기 1~3을 모두 적용했다고 가정하고 시작한다.
그리고, notepad2 컴파일 삽질기 부록#5.1 : context-menu 통합 및 수정은 적용하지 않은 상태에서 시작한다.


수정 대상파일은 resource.h, Notepad2.rc, Notepad2.c의 3개이며, 모두 src 폴더에 저장되어 있다.


1. resource.h

다음 한 줄을 추가.
#define IDM_REGISTRY_ALL            40690


2. Notepad2.rc

IDR_MAINWND MENU DISCARDABLE에서 아래 내용을 찾는다.
MENUITEM "Propert&ies...",              IDM_FILE_PROPERTIES
MENUITEM "Create &Desktop Link",        IDM_FILE_CREATELINK
MENUITEM SEPARATOR
POPUP "&Favorites"
BEGIN
    MENUITEM "&Open Favorites...\tAlt+I",   IDM_FILE_OPENFAV
    MENUITEM "&Add Current File...\tAlt+K", IDM_FILE_ADDTOFAV
    MENUITEM "&Manage...\tF9",              IDM_FILE_MANAGEFAV
END
이 부분을 아래와 같이 수정한다.
MENUITEM "Propert&ies...",              IDM_FILE_PROPERTIES
MENUITEM SEPARATOR
MENUITEM "Create &Desktop Link",        IDM_FILE_CREATELINK
MENUITEM "Add context-menu to all file type", IDM_REGISTRY_ALL, CHECKED
MENUITEM SEPARATOR
POPUP "&Favorites"
BEGIN
    MENUITEM "&Open Favorites...\tAlt+I",   IDM_FILE_OPENFAV
    MENUITEM "&Add Current File...\tAlt+K", IDM_FILE_ADDTOFAV
    MENUITEM "&Manage...\tF9",              IDM_FILE_MANAGEFAV
END

구분자 하나와 context-menu를 추가하는 내용임.


3. Notepad2.h

새로운 함수 하나를 추가.
void CheckRegistry();


4. Notepad2.c

파일 앞부분의 변수 선언부에 다음 내용을 추가.
BOOL      bRegistryUnknownType;
BOOL      bRegistryAllType;


다음, LRESULT CALLBACK MainWndProc(HWND hwnd,UINT umsg,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
case WM_COMMAND:
  return MsgCommand(hwnd,wParam,lParam);
여기에 아래와 같이 한 줄을 추가한다.
case WM_COMMAND:
  CheckRegistry();
  return MsgCommand(hwnd,wParam,lParam);


다음, void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
EnableCmd(hmenu,IDM_EDIT_SELTONEXT,i);
EnableCmd(hmenu,IDM_EDIT_SELTOPREV,i && lstrlenA(efrData.szFind));
EnableCmd(hmenu,IDM_EDIT_REPLACE,i /*&& !bReadOnly*/);

CheckCmd(hmenu,IDM_VIEW_USE2NDDEFAULT,Style_GetUse2ndDefault(hwndEdit));
이 부분을 아래와 같이 수정한다. 1줄을 추가하는 것임.
EnableCmd(hmenu,IDM_EDIT_SELTONEXT,i);
EnableCmd(hmenu,IDM_EDIT_SELTOPREV,i && lstrlenA(efrData.szFind));
EnableCmd(hmenu,IDM_EDIT_REPLACE,i /*&& !bReadOnly*/);

CheckCmd(hmenu,IDM_REGISTRY_ALL,bRegistryAllType || bRegistryUnknownType);

CheckCmd(hmenu,IDM_VIEW_USE2NDDEFAULT,Style_GetUse2ndDefault(hwndEdit));


다음은 LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam)의 수정임.
이 함수는 전체가 하나의 switch-case 문으로 되어있는데, 이 case 중에 IDM_REGISTRY_ALL을 추가함.
case IDM_REGISTRY_ALL:
// Register registry - type all
if (bRegistryAllType || bRegistryUnknownType)
{
if (bRegistryAllType)
{
  //WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_CLASSES_ROOT, L"*\\shell\\Open with notepad2\\command");
RegDeleteKey(HKEY_CLASSES_ROOT, L"*\\shell\\Open with notepad2");
bRegistryAllType = FALSE;
}
if (bRegistryUnknownType)
 {
//WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_CLASSES_ROOT, L"Unknown\\shell\\Open with notepad2\\command");
RegDeleteKey(HKEY_CLASSES_ROOT, L"Unknown\\shell\\Open with notepad2");
bRegistryUnknownType = FALSE;
}
}
else
{
HKEY key1;
LONG res = RegCreateKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2",&key1);
bRegistryAllType = TRUE;
if (res == ERROR_SUCCESS)
{
WCHAR cmd[] = L"Open with &notepad2";
RegSetValue(key1, NULL, REG_SZ, cmd, wcslen(cmd));
}
else bRegistryAllType = FALSE;
RegCloseKey(key1);

  res = RegCreateKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2\\command",&key1);
if (res == ERROR_SUCCESS)
{
WCHAR cmd[MAX_PATH + 4];
WCHAR path[MAX_PATH];
int len;
GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
len = wsprintf(cmd, L"%s %%1", path);
RegSetValue(key1, NULL, REG_SZ, cmd, len);
}
else bRegistryAllType = FALSE;
RegCloseKey(key1);

if (!bRegistryAllType)
{
MessageBeep(MB_ICONEXCLAMATION);
}
}
break;


마지막으로 함수를 하나 추가한다.
void CheckRegistry()
{
    HKEY key;
    LONG res;

    res = RegOpenKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2",&key);
    if(res == ERROR_SUCCESS)
            bRegistryAllType = TRUE;
    else    bRegistryAllType = FALSE;
    RegCloseKey(key);

    res = RegOpenKey(HKEY_CLASSES_ROOT,L"unknown\\shell\\Open with notepad2",&key);
    if(res == ERROR_SUCCESS)
            bRegistryUnknownType = TRUE;
    else    bRegistryUnknownType = FALSE;
    RegCloseKey(key);
}

이렇게 수정하면 아래처럼 File 메뉴에서 context-menu 메뉴 하나가 표시된다.

사용자 삽입 이미지

context-menu가 하나만 나오도록 수정. ^^;


디카 메모리가 부족하다고 풍경을 마음에 담을 순 없다! (캐궁극 버전)

by BLUEnLIVE | 2009/10/14 04:17

이번에도 okto님의 꼬득임에 문득 넘어가버려 기능 추가. OTL (이번에도 너는 이미 뜯어고치고 있다!) 내가 킹크랩을 반드시 뺏어먹고야 말...


디카 사진의 크기를 줄여주는 프로그램에 jpeg SIMD extension 라이브러리를 적용했다.
덕분에 기존 버전에 비해 속도가 엄청나게 빨라졌음은 물론, 파일의 크기가 훨씬 작아졌다.

기존 버전도 120,832바이트라는 엄청나게 작은 크기를 보여줬지만, 이 버전은 단  98,304바이트밖에 되지 않는다!

이 프로그램은 정말 작기 때문에 여행을 갈 때 디카 메모리 한 구석에 넣어두고 쓰면 유사시에 큰 도움이 될 것이다!


물론, 실행 화면은 아래처럼 거의 바뀐 게 없다. ㅎㅎ


notepad2 컴파일 삽질기 부록#11 : 포트란 스킴 추가

회사에서 일을 하면서 포트란 소스를 C++로 변환할 일이 생겼다.
notepad2에서 포트란 스킴을 지원하는 줄 알았는데, 지금 보니 지원하지 않았다. 헐~

그래서 포트란 스킴을 추가하기로 했다.
notepad2에서 사용하는 Scintilla에서는 포트란 스킴은 지원하지만, 이를 notepad2에서 사용하지 않는 것이다.
따라서, 이를 사용하기 위해서는 조금만 수정하면 된다.

포트란 스킴을 사용하려면 아래와 같이 수정하면 된다.


1. Notepad2.rc 수정

아래와 같은 부분을 찾는다.
    63016                   "Batch Files"
    63017                   "Diff Files"
    63018                   "SQL Query"
    63019                   "Python Script"
    63020                   "Apache Config Files"
    63021                   "PowerShell Script"
END

여기에 아래와 같이 한 줄을 추가한다. 위치는 물론 END 바로 앞이며, 언어를 이미 추가한 경우 적절히 알아서 한다.
    63022                   "Fortran Source Code"
   63023                   "Fortran77 Source Code"


2. Styles.h 수정

아래와 같은 부분을 찾는다.
// Number of Lexers in pLexArray
#define NUMLEXERS 30

NUMLEXERS의 값을 둘 증가시킨다. 아래처럼.


#define NUMLEXERS 32


3. Styles.c 수정 #1

아래와 같은 부분을 찾는다.
// This array holds all the lexers...
PEDITLEXER pLexArray[NUMLEXERS] =
{
  &lexDefault,

바로 앞에 아래와 같은 코드를 추가한다.



// FORTRAN added by BLUEnLIVE - 25th June 2010
KEYWORDLIST KeyWords_FORTRAN = {
"abs achar acos acosd adjustl adjustr aimag aimax0 aimin0 aint ajmax0 "
"ajmin0 akmax0 akmin0 all allocated alog alog10 amax0 amax1 amin0 amin1 "
"amod anint any asin asind associated atan atan2 atan2d atand bitest bitl "
"bitlr bitrl bjtest bit_size bktest break btest cabs ccos cdabs cdcos cdexp "
"cdlog cdsin cdsqrt ceiling cexp char clog cmplx conjg cos cosd cosh count "
"cpu_time cshift csin csqrt dabs dacos dacosd dasin dasind datan datan2 "
"datan2d datand date date_and_time dble dcmplx dconjg dcos dcosd dcosh "
"dcotan ddim dexp dfloat dflotk dfloti dflotj digits dim dimag dint dlog "
"dlog10 dmax1 dmin1 dmod dnint dot_product dprod dreal dsign dsin dsind "
"dsinh dsqrt dtan dtand dtanh eoshift epsilon errsns exp exponent float "
"floati floatj floatk floor fraction free huge iabs iachar iand ibclr ibits "
"ibset ichar idate idim idint idnint ieor ifix iiabs iiand iibclr iibits "
"iibset iidim iidint iidnnt iieor iifix iint iior iiqint iiqnnt iishft "
"iishftc iisign ilen imax0 imax1 imin0 imin1 imod index inint inot int "
"int1 int2 int4 int8 iqint iqnint ior ishft ishftc isign isnan izext jiand "
"jibclr jibits jibset jidim jidint jidnnt jieor jifix jint jior jiqint jiqnnt "
"jishft jishftc jisign jmax0 jmax1 jmin0 jmin1 jmod jnint jnot jzext kiabs "
"kiand kibclr kibits kibset kidim kidint kidnnt kieor kifix kind kint kior "
"kishft kishftc kisign kmax0 kmax1 kmin0 kmin1 kmod knint knot kzext lbound "
"leadz len len_trim lenlge lge lgt lle llt log log10 logical lshift malloc "
"matmul max max0 max1 maxexponent maxloc maxval merge min min0 min1 "
"minexponent minloc minval mod modulo mvbits nearest nint not nworkers "
"number_of_processors pack popcnt poppar precision present product radix "
"random random_number random_seed range real repeat reshape rrspacing rshift "
"scale scan secnds selected_int_kind selected_real_kind set_exponent shape "
"sign sin sind sinh size sizeof sngl snglq spacing spread sqrt sum "
"system_clock tan tand tanh tiny transfer transpose trim ubound unpack verify",
"access action advance allocatable allocate apostrophe assign assignment "
"associate asynchronous backspace bind blank blockdata call case character "
"class close common complex contains continue cycle data deallocate decimal "
"delim default dimension direct do dowhile double doubleprecision else elseif "
"elsewhere encoding end endassociate endblockdata enddo endfile endforall "
"endfunction endif endinterface endmodule endprogram endselect endsubroutine "
"endtype endwhere entry eor equivalence err errmsg exist exit external file "
"flush fmt forall form format formatted function go goto id if implicit in "
"include inout integer inquire intent interface intrinsic iomsg iolength "
"iostat kind len logical module name named namelist nextrec nml none nullify "
"number only open opened operator optional out pad parameter pass pause "
"pending pointer pos position precision print private program protected "
"public quote read readwrite real rec recl recursive result return rewind "
"save select selectcase selecttype sequential sign size stat status stop "
"stream subroutine target then to type unformatted unit use value volatile "
"wait where while write",
"", "", "", "", "", "", "" };

EDITLEXER lexFORTRAN = { SCLEX_FORTRAN, 63022, L"Fortran Source Code", L"f; for; f90; f95", L"", &KeyWords_FORTRAN, {
    { STYLE_DEFAULT, 63126, L"Default", L"", L"" },
    { SCE_F_COMMENT, 63127, L"Comment", L"italics; fore:#008000", L"" },
    { SCE_F_NUMBER, 63130, L"Number", L"bold; fore:#0000FF", L"" },
    { MULTI_STYLE(SCE_F_STRING1,SCE_F_STRING2,SCE_F_STRINGEOL,0), 63131, L"String", L"fore:#008000", L"" },
    { MULTI_STYLE(SCE_F_OPERATOR,SCE_F_OPERATOR,0,0), 63132, L"Operator", L"bold", L"" },
    { SCE_F_IDENTIFIER, 63281, L"Identifier", L"bold; fore:#C80000", L"" },
    { SCE_F_WORD, 63236, L"Word", L"fore:#0000A0; bold", L"" },
    { SCE_F_WORD2, 63260, L"Word2", L"fore:#0000FF; bold", L"" },
    { SCE_F_WORD3, 63203, L"Word3", L"fore:#008000; bold", L"" },
    { SCE_F_PREPROCESSOR, 63133, L"Preprocessor", L"fore:#FF8000", L"" },
    { SCE_F_LABEL, 63235, L"Label", L"fore:#000000; back:#FFFFD1; bold", L"" },
    { SCE_F_CONTINUATION, 63257, L"Continuation", L"back:#FFC0C0", L"" },
    { -1, 00000, L"", L"", L"" } } };

EDITLEXER lexFORTRAN77 = { SCLEX_F77, 63023, L"Fortran77 Source Code", L"f77", L"", &KeyWords_FORTRAN, {
    { STYLE_DEFAULT, 63126, L"Default", L"", L"" },
    { SCE_F_COMMENT, 63127, L"Comment", L"italics; fore:#008000", L"" },
    { SCE_F_NUMBER, 63130, L"Number", L"bold; fore:#0000FF", L"" },
    { MULTI_STYLE(SCE_F_STRING1,SCE_F_STRING2,SCE_F_STRINGEOL,0), 63131, L"String", L"fore:#008000", L"" },
    { MULTI_STYLE(SCE_F_OPERATOR,SCE_F_OPERATOR,0,0), 63132, L"Operator", L"bold", L"" },
    { SCE_F_IDENTIFIER, 63281, L"Identifier", L"bold; fore:#C80000", L"" },
    { SCE_F_WORD, 63236, L"Word", L"fore:#0000A0; bold", L"" },
    { SCE_F_WORD2, 63260, L"Word2", L"fore:#0000FF; bold", L"" },
    { SCE_F_WORD3, 63203, L"Word3", L"fore:#008000; bold", L"" },
    { SCE_F_PREPROCESSOR, 63133, L"Preprocessor", L"fore:#FF8000", L"" },
    { SCE_F_LABEL, 63235, L"Label", L"fore:#000000; back:#FFFFD1; bold", L"" },
    { SCE_F_CONTINUATION, 63257, L"Continuation", L"back:#FFC0C0", L"" },
    { -1, 00000, L"", L"", L"" } } };

여기서 48, 63행의 63022, 63023은 위의 1번에서 추가한 값과 똑같아야 한다.


4. Styles.c 수정 #2


위의 3번에서 찾은 내용은 아래와 같이 생겼다.

PEDITLEXER pLexArray[NUMLEXERS] =
{
  &lexDefault,
  &lexHTML,
  &lexXML,
  &lexCSS,
  &lexJS,
  &lexVBS,

이 배열의 맨 마지막에 아래와 같은 내용을 추가한다.


  &lexFORTRAN,
  &lexFORTRAN77


이러한 수정이 적용된 화면은 아래와 같다.

포트란이 지원되는 메모장2!


2010년 6월 25일 금요일

멀티 코어 CPU에서 코어 별 사용량 측정하는 법 (VS2008)

Visual C++에서 CPU의 사용량을 확인할 때는 PDH(Performance Data Helper)를 이용하는 것이 기본이다.
그런데, 그 방법으로는 멀티코어 CPU에서의 코어 별 사용량을 알 수가 없다.

해결책을 뒤진 결과 Code Project: Getting CPU Usage in a Multiprocessor Machine라는 글을 찾을 수 있었다.
(이 글에 따르면) WMI(Windows Management and Instrumentation)를 이용해야 하며, Win32_PerfRawData_PerfOS_Processor 클래스를 사용하면 원하는 결과를 얻을 수 있다.

위의 글을 보면 작업 순서가 아주 잘 나와있다.
하지만, 그런다고 매번 프로그램을 그 순서에 따라 작성할 수는 없어 간단하게 CCPUUsage라는 클래스를 만들었다.


class CCPUUsage
{
public:
    ...

protected:
    ...

public:
    double *dUsage;
    BOOL GetInitValues();
    void GetValues();

    int inline iNumProc()
    { return iNproc; }

    BOOL inline bOK()
    { return bInitialized; }
};


위의 코드는  클래스 헤더의 일부이며, 이 중 public: 부분만 정리한 것이다.
CCPUUsage 클래스의 인스턴스를 생성한 뒤에 측정을 시작하는 위치에서 GetInitValues()를 호출한 뒤, 측정을 끝낼 위치에서 다시 GetValues()를 호출하면 된다.


{
    CCPUUsage cpuusage;
    BOOL bOK = cpuusage.GetInitValues();
    //작업 시작
    ...
    //작업 종료
    if (bOK)
    {
        cpuusage.GetValues();
        for (int i=0; i<=cpuusage.iNumProc(); i++)
            printf("%g\n", cpuusage.dUsage[i]);
    }
}


결과는 dUsage[코어개수+1]에 저장되는데, [0~코어개수-1] 까지는 각 코어의 사용량이고, [코어개수]엔 평균치가 저장된다.
즉, 쿼드코어의 경우 dUsage[0~4]에 저장되며, dUsage[0~3]은 각 코어의 사용량, dUsage[4]는 평균치가 저장된다.

아래 압축파일을 다운받아 사용하면 되며, 이 코드는 Visual C++ 2008에서 테스트되었다.


덧. 참고한 원래 소스는 Visual C++ 6.0 용이었는데, 실행이 제대로 되지 않았다.
확인해보니 _wtol()가 내가 원하는 대로 동작하지 않은 것이 원인이었다.
결과가 ULONG의 범위를 벗어나면 오버플로우가 발생한 뒤에, 똑같은 값만 나오는 것이다.
그래서, ULONG 대신 __int64를 사용하고, _wtoi64() 사용해버렸다.

2010년 6월 22일 화요일

더 빠른 jpeg 라이브러리 jpeg-turbo 컴파일 삽질기

by BLUEnLIVE | 2010/06/21 07:49

Jpeg 파일을 프로그램에서 읽고 쓸 때 libjpeg 라이브러리를 많이 사용한다. 이 라이브러리는 안정적이고 널리 쓰이고 있지만, 속도가 다소...


VS 2008/2010에서 사용할 수 있는 빠른 jpeg 라이브러리를 찾다가 turbo-jpeg이란 라이브러리를 찾게 되었다.
x86 SIMD ext for IJG JPEG를 개선해서 만든 라이브러리답게, SIMD ext에 비해서 약간 빠르다.

ㅎㄷㄷ한 성능 향상. 아싸!


BUILDING.txt 파일에 컴파일하는 법이 나와있는데, 간략히 간추리면 이렇다.

1. GNU Make v3.7 이상 준비: MSYS나 Cygwin에 들어있음. (그렇다! nmake는 쓰지 않는다!)
2. Windows SDK for Windows Server 2008 and .NET Framework 3.5 준비
3. NASM v0.98 이상 준비: SIMD ext에서와 마찬가지로 어셈블러로 NASM을 사용.
4. make -f win/Makefile

VS 2008과 2010 모두에서 동일한 방법으로 컴파일 가능하며, 성능은 비슷하다.


덧1. 최근 몇 가지 이유로 컴파일러를 VC++ 6.0에서 VS 2008로 이전할 예정인데, 적절한 jpeg 라이브러리를 찾은 듯.
덧2. SIMD ext는 VC++ 6.0에서만 컴파일 가능하고, jpeg-turbo는 VC++ 2008 이상에서만 가능한 듯.
덧3. make는 GNU Make를 쓰지만, link는 VS 2008의 link를 써야 함. path에 지정했다 잠시 삽질 함.

2010년 6월 21일 월요일

빠른 jpeg 라이브러리 x86 SIMD ext for IJG JPEG 컴파일 삽질기

Jpeg 파일을 프로그램에서 읽고 쓸 때 libjpeg 라이브러리를 많이 사용한다.
이 라이브러리는 안정적이고 널리 쓰이고 있지만, 속도가 다소 느린 편이다.

꿀뷰 등의 빠른 이미지 뷰어에서 사용되는 x86 SIMD ext for IJG JPEG 라이브러리가 있다.
이 라이브러리는 libjpeg 6b를 확장해서 일부를 어셈블러로 작성한 것인데, 속도가 상당히 빠르다.
(현재 libjpeg는 8b 까지 공개됨)

이 라이브러리를 컴파일해보고, 이것이 libjpeg 8b에 비해 얼마나 빠른가를 확인해봤다.

컴파일 시에 특별히 준비할 것은 없고, NASM을 사용할 수 있도록 설치하기만 하면 된다.
준비사항은 아래와 같다.

1. NASM 설치
2. 실행파일의 파일명을 nasm.exe에서 nasmw.exe로 변경

Visual Studio 6.0과 Visual Studio 2010에서 같은 코드를 실행시켜 성능을 비교해봤다.


비교는 위 실행화면처럼 연산을 20회 반복한 뒤에 평균시간을 쟀다.
대상은 VC++6에서 컴파일한 6b-SIMD8b 그리고, VC++10에서 컴파일한 8b.
(VC++10에선 6b-SIMD 컴파일 실패했음)


역시 6b-SIMD가 어셈블러로 되어있는지라 상당히 빠르다.

1. 6b-SIMD는 8b에 비해 3배 가까이 빠름
2. 8b도 VC++10에서 컴파일한 것이 10% 정도 빠름
3. VC++10에서 6b-SIMD 컴파일 실패. 성공한 고수님들 계시면 내공 전수 좀…


2010년 6월 20일 일요일

OpenMP로 처음으로 만들어본 멀티코어 프로그램


회사에서 멀티코어 프로그래밍을 해야 될 상황이 도래해서 이리저리 솔루션을 찾아봤다.
그런데, 인텔에서 판매하는 수학 라이브러리 등과는 달리, OpenMP라는 솔루션은 이미 VC++에 내장되어 있었다!
(인텔 Math Library는 멀티코어를 지원하며, 성능이 짱이라고 함. 그런다고 회사에서 사줄리는…)

관련 문서를 좀 읽어본 뒤에 처음으로 멀티코어 프로그래밍에 도전했다.

대상은 이미지 리샘플링.
기존에 만들어 둔 Lanczos3 리샘플을 멀티코어 버전으로 수정했더니, 성능 향상이 눈에 확 띤다.

내 Quad-core에서 (당연히) 쓰레드를 4개로 돌릴 때가 최강의 성능을 보임


역시 예상했던 대로 쓰레드의 수를 5개(코어의 갯수) 이상으로 하는 것은 아무런 의미가 없다.

일단 뭔가 한 발짝을 내딛은 것 같아 뿌듯함.

덧. 이와 함께 jpeglib 6b의 SIMD 확장 버전을 VC++ 2008/2010에서 컴파일해보다 실패했다.
VC++ 6에서는 완벽하게 컴파일되는데, 뭐가 문제일까?
혹시 고수님 계시면 도움 좀 부탁드립니다. (굽신굽신)

2010년 6월 12일 토요일

[A-특공대]: 80년대의 향수를 느낄 수 있는 리부트


심야로 [A-특공대]를 보고 왔다.
80년대의 향수를 물씬 느낄 수 있는 리부트였다. (그렇다! 리메이크가 아니다!)

영화를 보면서 느낀 단상들을 정리함.

1. 시작 전에 쓰레기 S 기업의 갤럭시 A 광고를 보여줬다. 갤럭시 S도 아니고... 이것들은 역시 개념이 없음.

2. 일단, 영화는 정말로 재미있음!

3. TV 시리즈 전체에서 5명밖에 안 죽었던 것에 비해 영화에선 초큼 많이 죽음.

4. 리메이크가 아니라 리부트이며, 이로 인해 베트남전이 아니라 이라크전으로 배경이 바뀜.

5. 이로 인해 이들이 쫓기게 된 이유가 바뀌지만, 실마리를 가진 유일한 사람이 죽는다는 점은 원작과 동일함.

6. 언제나 영화에서 CIA는 좋지 않은 놈으로 나오는데, 이번에도 크게 다르지 않음. 그에 비하면 007이 있는 MI6는 행복함.

7. 시나리오는 의외로 허술하지 않았음. 게다가, 80년대의 향수가 느껴지는, 그야말로 [A-특공대] 맞춤형임!

8. 오프닝 헬리콥터씬, 탱크씬 등은 아예 말이 안 되는데, 이 영화의 특성을 그대로 보여줌. 리얼리즘 필요 없고 닥치고 액션!

9. 무려 웨타 디지털이 특수효과에 참여했으면서도 일부 CG는 티가 좀 남. 이 역시 이 영화의 특성을 그대로 보여줌.

10. BA의 애마(GM 밴)는 너무나 너무나 반가웠음. 이게 없으면 [A-특공대]가 아닌 거임. 그러나... ㅠ.ㅠ

11. 원작의 한니발은 설정에 비해 터프함이 많이 느껴지지 않았는데, 리암 형님은 그야말로 마초적임. 짱 맘에 듦.

12. BA는 초반부터 강제전역을 한 상태로 나오는데, 다음 장면에선 현역임. 그 새 복직되었나?

13. 원작의 BA는 장신구를 치렁치렁 매달았는데, 영화에선 없었음. 아무래도 현역 신분 때문인 듯.

14. 멋쟁이(Face)는 원작의 더크 형님보다 잘생긴 얼굴은 아니지만, 매력 만점으로 캐릭터 싱크로는 100%임

15. BA의 고소공포증을 설명하는 장면이 있는데, 원작에서 빠진 설명을 보충한다는 느낌이라 반가웠음.

16. 원작의 특성 중 하나가 4인방이 균형있게 등장한다는 건데, 영화 역시 4인방이 골고루 등장해 원작을 잘 승계함.

17. 엔딩 쿠키에 그분들이 나오셨음. 나이는 많이 드셨지만, 반갑기 짝이 없었음.

18. BA 역의 퀸튼 잭슨은 UFC에서 에반스와 경기할 예정이었으나, 영화와 관련되어 UFC 측과 싸운 뒤 UFC에서 은퇴함.
     하지만, 촬영이 끝난 뒤 다시 UFC로 복귀하여 에반스와 경기를 하여 심판 만장일치 판정패함.

19. 만하임(Manheim), 프랑크프루트, 스위스(취리히의 호수)는 얼마나 반갑던지!

20. 액션은 전체적으로 나쁘진 않았지만, 격투씬은 너무 가깝게 촬영했다는 느낌. TV 시리즈의 느낌을 살리기 위해서일까?

마지막으로 1980년대 오리지널의 오프닝을 감상하시라!


쓰레기 기업의 쓰레기 프리젠테이션: "갤럭시S"

언제나 새 제품을 출시하면 이전 제품에 대한 지원을 절대 하지 않으며, 노동자의 목숨을 파리 목숨보다 못하게 생각하는 쓰레기 모 기업(어딘지는 모름)에서 새 제품을 출시한답시고 발표회를 가졌단다.

발표회 동영상을 보니 경쟁제품(이라고 자신들만 생각하는, 그러나 정작 그 쪽에선 아오안인) 발표회와 수준 차이가 너무 나서 헛웃음이 나온다.


이 동영상을 보며 느낀 몇 가지 단상들.

1. 넌 직업이 뭐냐? 발표 전문가라기 보기엔 프리젠테이션이 어설프고, 엔지니어라 보기엔 뭘 모르는 것 같다.

2. 뭘 팔려는 거냐? 팔려는 게 뭔지를 모르겠다.

3. 아이폰 인터페이스를 그대로 베꼈는데, 이게 20년 기술력의 집합체고, 건희제의 자존심이냐?
   좀도둑도 아니고 원...

4. 그놈의 1GHz 타령 좀 그만 해라! 숫자를 떠든다고 그게 신뢰성으로 연결되는 게 아니다!

5. 오늘 [A특공대]를 보는데, 갤럭시A 광고가 나왔다. 싸우자는 거냐?

이 프리젠테이션은 정말이지 구멍가게 수준의 프리젠테이션이다.
건희제야 이거 보고 좋아했을지도 모르겠지만, 정작 소비자에겐 이뭐병 수준...

2010년 6월 10일 목요일

아이폰4의 FaceTime 동영상은 샘 맨데스가 감독

MGM이 휘청휘청하면서 차기 007 영화의 제작 여부가 불투명해졌다.
덕분에 주연을 맡은 다니엘 크레이그나 감독인 샘 멘데스의 행보도 불투명...

어쨌거나... 애플 아이폰4 관련 영상들을 보면 화상통신 프로그램인 FaceTime의 소개 영상은 샘 멘데스가 감독했다.
그는 [아메리칸 뷰티], [로드 투 퍼디션] 등의 감독다운 멋진 영상을 보여준다.

우리나라에선 특별하지도 않은 화상통신인데, 이 영상을 보면 마치 새로운 기능을 보는 듯한 느낌을 준다.
이성보다는 감성에 호소하는 영상 덕분인 듯.


WWDC 2010 아이폰4 발표 동영상 (한글자막)

트위터에서 아이폰4 발표 동영상의 한글자막 버전이 올라와서 블로그에 링크함.


#1.


#2.


#3.


#4.


#5.



덧1. 아이폰 3GS를 사지 않고 참았는데, 아이폰4 발표를 보니 급땡긴다. 헐~

덧2. 프리젠테이션 자체만 봐도 S모 기업의 "외계 S" 프리젠테이션과는 비교가 되지 않는다.

아직도 수익을 내고 있는 [아바타]!

네티즌 님과 얘기를 하다가 [아바타]가 아직 극장에서 내려오지 않았단 루머(?)를 들었다.
별로 신경을 쓰지 않았던 건데 혹시나하고 boxofficemojo를 찾아봤다.

그런데, 정말로...

6월 10일 기준, 마지막으로 집계된 데이터는 6월 7일까지임


아직까지 수익을 내고 있다!
그리고, 현재까지의 세계 수익은 무려...

Worldwide: $2,727,213,502


27억 달러를 넘는다!
2위인 [타이타닉]이 18억달러라는 점을 생각해보면 이젠 수익에 있어서는 누구도 추격이 불가능한 수준이 될 것 같다.
게다가, 루머대로 11월 경에 재개봉을 한다면 수익은 손쉽게 30억 달러를 넘길 것 같다.

흠좀무. 카메론 감독 당신 대체 어느 별에서 온 거요? 네?

2010년 6월 2일 수요일

하멜른에서 돌아오며 잠시 들른 하노버

하멜른에서 돌아오면서 든 생각이... 하노버를 스쳐지나간 건 수없이 많은데, 정작 한번도 돌아보지 않았다는 거.
그래서, 이왕 온 거 하노버에도 잠시 들러 한 시간 정도만 주변을 둘러보기로 했다.

여기는 S-Bahn. 짱이는 충전중... 충전중...


역 앞에 있는 기마상에서 사진을 찍는데, 린이는 좋아서 어쩔줄을 모른다.
(짱이는 자다 막 일어나서 촬영을 거부하며 버로우 중)

아유~ 귀여워... 귀여운 표정이 지대로임


분수대에서 펄쩍 뛰며 노는 모습이 너무나 예쁘다.


역에서 한 블럭만 아래로 내려오면 쇼핑의 천국 크뢰프케 광장이 있다.
이 곳의 상징인 크뢰프케 시계(Kröpcke Clock) 앞에서 한 컷.


조금 내려오면 오페라 하우스(Opernhaus)가 있다.
바그너의 발퀴레(Die Walküre)를 공연하고 있는 것 같다. 뭐 그렇다고...


멀리 마르크트 교회(Marktkirche)가 보인다.
비가 계속 오다말다 해서 저거까지만 보고 돌아가기로 결정.


좀 더 가까이에서 보니 악마의 별이 보인다. (오각형 + 원)
조금은... 뭥미 싶다...


가는 길에 비가 억수같이 쏟아져 잠시 비를 피하는데, 많이 보던 것이 보인다.
브레멘에도 저것과 비슷한 것이 있다. (아니, 똑같은 건가?)


이윽고 도착한 마르크트 교회.


그리고, 그 앞에 있는 구 시청사(Altes Rathaus).
지금은 식당으로 사용하고 있는 것 같다.


교회 근처 그루펜거리(Grupenstraße)에 있는 아버지와 아들 분수.


구 시청사 앞에서 멀리 보이는 아에기디엔 교회(Aegidien Kirche)를 배경으로 한 컷 찍은 후 돌아가기로 결정.
비가 계속 오기 때문에 다들 너무 힘들다. 헥헥.


역으로 오다가 카르마르슈 거리(Karmarschstraße)란 곳에 분수가 하나 있어 한 컷.


우리 가족은 하노버 역에서 산 크로와상을 먹으며 돌아왔다.

처묵처묵


마침내 돌아온 브레멘 중앙역. 반갑다!

이제 한동안 보기 힘들 브레멘 중앙역. 츄스~


슈토키 산장 소개: 스위스 부록

스위스 여행의 가장 큰 적은 비싼 물가다.
식사의 경우 좀 저렴한 음식을 사 먹으면 된다지만, 숙박비의 경우 싼 숙박시설을 찾는 것이 쉽지는 않다.

스위스에서 저렴하게 숙박을 하고 싶으면 슈토키 산장(Matratzenlager Stoki)을 찾으면 된다.
1박에 성인 15유로, 어린이 8유로로 우리 가족은 2박을 단 92유로에 해결했다.

이 곳의 장점은 대략 아래와 같다.

- 저렴한 가격 (몽땅 도미토리, 1박에 15유로, 어린이 반값)
- 주인 할머니가 전혀 터치를 하지 않음 (아예 옆집에 사심)
- 엄청나게 많은 접시, 그릇, 냄비, 쿡탑 (할머니가 그릇 콜렉터라는 루머가... ㅎㅎ)
- 투숙객의 대부분이 한국사람이라 여행 정보를 아주 쉽게 얻을 수 있음
- 주변 경치가 너무 좋아 아침에 창문만 열면 절경 감상 가능
- (스위스에서 종종 불편을 겪는) 스위스 전용 220V 콘센트 외에 유럽/한국형 콘센트 다수 설치

하지만, 단점도 있는데, 위에서 읽었듯이 여긴 도미토리 밖에 없다.
즉, 스위스의 절경을 즐기고 싶은 신혼부부나, 나만의 조용한 공간을 찾는 분이라면... 과감히 패스해야 한다.
게다가, 무선 인터넷과 같은 문명은 전무하다. 인터넷이 없으면 못 사시는 분들 역시 패스해야 한다.
물론, 아침 식사도 제공되지 않는다.

참고로, 마트에서 살 수 있는 식재료는 그렇게 비싼 편은 아니며, 우리나라 사람이 먹을만한 재료들은 충분히 있기 때문에 사서 요리하는 것을 추천.

한쪽은 4인 2층 침대 (총 8명) / 우리 가족은 여길 독점했음 ㅎㅎ


반대쪽은 6인 2층 침대 (총 12명)


여길 찾아가려면 일단 라우터브루넨 역에서 시내 반대쪽 방향으로 나와서 냇가를 따라가다 다리를 건너면 된다.
(뭔가 좀 어려운가? 역에서 표지판을 찾아보면 되며, 역무원 아무라도 붙잡고 물어보면 다 안다)

막상 가보면 찾는 것은 그리 어렵지 않다.
참고로, Stoki는 Stokistraße라는 거리 이름에서 온 것이라 표지판에 표시가 잘 되어있다.


주의해야할 점이 2가지 있다.
첫째는, 주인 할머니는 노터치하지만, 투숙객의 대부분이 한국사람인 관계로 밤에 좀 시끄러웠던 경우가 있었나보다.
밤 10시 이후엔 조용히하라는 경고문이 붙어있는데, 경찰서에서 붙인 거다. ㄷㄷㄷ


나머지 하나는 체크인이나 예약하러 가는 집...
숙소 옆에 있는 집인데, 보다시피 입구가 2개이고, 오른쪽은 다른 사람이 사는 곳이다.
오른쪽 집에서 초인종 누르면 큰 실례다. 주의해서 왼쪽 초인종을 누르면 된다.


주인 할머니는 깔끔한 성격에 영어도 잘 하셔서 여행객들과 대화하는데 문제는 없다.
단, 아침에 설거지가 잘 되어있지 않거나, 손님 마음대로 자리를 바꾸는 등의 돌출행위를 상당히 싫어한다.
(이는 사실, 여기 뿐만 아니라, 유럽 전체가 다 그렇다)

우린 묵기 1주일 전에 직접 찾아가서 예약을 했는데, 직접 숙소로 데려가서는 여기를 쓸 거라고 얘기해주셨다.
그리고, 때가 되어 다시 가니... 딱 그 자리에 아래와 같은 표시가 되어 있었다.
(즉, 이 자리에 다른 사람이 슬쩍 들어오면 할머니의 공격을 받게 된다. ㅎㅎ)


주방은 아래와 같이 생겼는데, 쿡탑이 2개 있고, 냄비도 많아서 밥 짓고 국 끓이는데 아무 문제 없다.


그릇, 접시, 수저, 컵 등은 아래와 같이 잔뜩 있는데, 심지어 퐁듀 먹을 때 쓰는 포크도 잔뜩 마련되어있다.


그리고, 스위스에선 다른 유럽 국가와는 조금 다른 규격의 콘센트를 사용한다.
아래 사진처럼 생긴 3구인데, 문제는 우리나라나 다른 유럽 국가들보다 구멍의 크기가 작다는 것이다.


그런데, 여기 오면 아래 사진처럼 무려 6개의 유럽/한국형 콘센트가 준비되어있다. ㅎㅎ

문어발이긴 하지만, 죄다 꽂는 건 디카, 핸폰 충전 뿐이라 안전 문제는 없음


실내에 식탁이 충분히 구비되어 있음은 물론이고...


실외에도 자리가 좀 있어, 맑은 공기를 만끽하며 식사를 할 수 있다.


간혹, 스위스 사람들이 정말 돈만 밝히고 무뚝뚝하냐는 질문을 들었는데, 전혀 그렇지 않다.
언제나 표정이 밝으며, 친절하기 짝이 없어 여행 중 도움이 필요할 땐 아무에게나 부담없이 물을 수 있다.
그리고, 돈에 대해 깔끔하고 정직해서 여러모로 "선진국의 풍모"를 느낄 수 있었다.

슈토키 산장 주인 할머니와 영수증을 쓰는데, 영수증에 상세한 가족 내역을 쓰는 칸이 있었다.
이건 뭐냐 물어보니까... 투숙객의 나이에 따라 세금이 다르게 매겨진다면서 정확하게 있는 그대로 적은 뒤 사본을 나에게 줬다.
이걸 돈만 밝히는 무뚝뚝함이라고 하면, 우리나라 치과나 모텔에서 현금 내면 할인해주는 건 정이 많아서란 얘기다.

중앙 하단의 박스가 세금 계산을 위한 칸. 근본부터 정직한 사람들이다!


저렴하고 편안하게 스위스를 즐길 분들께 슈토키 산장을 추천한다!


덧1. 전술했듯이, 슈토키는 거리 이름이다(Stokistraße).
그런데, 할머니의 이름이 스토키(슈토키도 아니고)로 알려져있으며, 방명록에는 서덕희 할머니란 한글 이름도 적혀있다. ㅎㅎ
할머니의 이름은 그레티 그라프-프로이츠(Greti Graf-Feuz)이다. 영수증에 정확히 적힌 이름이 왜 잘못 알려졌는지는...

덧2. 샤워실이 하나가 있고, 세면실이 있는데, 공동으로 사용해야 한다.
결국, 샤워실이나 침실의 구성을 보면 남녀 공용인 군대식이라 보면 딱 맞다. ㅎㅎ