Microsoft MVP성태의 닷넷 이야기
VC++: 87. 무시할 수 없는 Visual C++ 런타임 함수 성능 [링크 복사], [링크+제목 복사],
조회: 21764
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)

무시할 수 없는 Visual C++ 런타임 함수 성능

아래의 글에 달린 덧글 덕분에,

보이어-무어(Boyer-Moore) 알고리즘이 정말 빠를까?
; https://www.sysnet.pe.kr/2/0/1705

C 런타임 함수를 새롭게 보게 되었습니다. ^^

위의 글에서 strstr 함수가 boyer_moore 함수에 비해 굉장히 빠른 속도를 내는 것을 보았는데요. 덧글의 의견처럼 "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\strstr.c"에 해당하는 소스코드는 Visual C++에서 실행되는 strstr 함수가 아니었습니다.

이를 확인하는 방법은 간단합니다.

Visual C++ 프로젝트에서 "Runtime Library" 옵션을 "Multi-threaded Debug"로 바꾸고,

crt_func_debug_0.png

Visual Studio에서 디버깅을 시작한 다음 strstr 함수로 F11(Step-into)키를 눌러 진입하면 다음과 같이 소스 코드를 찾는 창이 뜹니다.

crt_func_debug_1.png

"f:\dd\vctools\crt\crtw32\string\i386\strstr.asm" 경로는 마이크로소프트 측에서 빌드했을 때의 경로이고, 우리는 그냥 strstr.asm 파일을 찾아서 매핑시켜주면 됩니다. Visual Studio 2013을 설치한 경우 다음의 경로에 있으니,

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src\intel\strstr.asm

이 파일을 맞춰주면 Visual Studio에서 assembly 코드에 대한 라인 단위 디버깅으로 진입합니다. 거기다, "Registers" 창을 띄워 놓으면 레지스터 값 확인까지 가능합니다. 이렇게!

crt_func_debug_2.png

참고로, strstr.asm의 소스 코드는 다음과 같습니다.

        page    ,132
        title   strstr - search for one string inside another
;***
;strstr.asm - search for one string inside another
;
;       Copyright (c) Microsoft Corporation. All rights reserved.
;
;Purpose:
;       defines strstr() - search for one string inside another
;
;*******************************************************************************

        .xlist
        include cruntime.inc
        .list

page
;***
;char *strstr(str1, str2) - search for str2 in str1
;
;Purpose:
;       finds the first occurrence of str2 in str1
;
;Entry:
;       char *str1 - string to search in
;       char *str2 - string to search for
;
;Exit:
;       returns a pointer to the first occurrence of string2 in
;       string1, or NULL if string2 does not occur in string1
;
;Uses:
;
;Exceptions:
;
;*******************************************************************************


__from_strstr_to_strchr proto

        CODESEG

        public  strstr

strstr  proc \
        str1:ptr byte, \
        str2:ptr byte

        OPTION PROLOGUE:NONE, EPILOGUE:NONE

        mov     ecx,[esp + 8]       ; str2 (the string to be searched for)
        mov     eax,[esp + 4]       ; str1 (the string to be searched)

        push    edi                 ; Preserve edi, ebx and esi
        push    ebx
        push    esi

;   .FPO (cdwLocals, cdwParams, cbProlog, cbRegs, fUseBP, cbFrame)
    .FPO      ( 0, 2, $-strstr, 3, 0, 0 )

; Include SSE2/SSE4.2 code paths for platforms that support them.
        include strstr_sse.inc

        mov     dl,[ecx]            ; dl contains first char from str2

        mov     edi,eax             ; str1 (the string to be searched)

        test    dl,dl               ; is str2 empty?
        jz      empty_str2

        mov     dh,[ecx + 1]        ; second char from str2
        test    dh,dh               ; is str2 a one-character string?
        jz      strchr_call         ; if so, go use strchr code

; length of str2 is now known to be > 1 (used later)
; dl contains first char from str2
; dh contains second char from str2
; edi holds str1

findnext:
        mov     esi,edi             ; esi = edi = pointers to somewhere in str1
        mov     ecx,[esp + 14h]     ; str2

;use edi instead of esi to eliminate AGI
        mov     al,[edi]            ; al is next char from str1

        add     esi,1               ; increment pointer into str1

        cmp     al,dl
        je      first_char_found

        test    al,al               ; end of str1?
        jz      not_found           ; yes, and no match has been found

loop_start:
        mov     al,[esi]            ; put next char from str1 into al
        add     esi,1               ; increment pointer in str1
in_loop:
        cmp     al,dl
        je      first_char_found

        test    al,al               ; end of str1?
        jnz     loop_start          ; no, go get another char from str1

not_found:
        pop     esi
        pop     ebx
        pop     edi
        xor     eax,eax
        ret

; recall that dh contains the second char from str2

first_char_found:
        mov     al,[esi]            ; put next char from str1 into al
        add     esi,1

        cmp     al,dh               ; compare second chars
        jnz     in_loop             ; no match, continue search

two_first_chars_equal:
        lea     edi,[esi - 1]       ; store position of last read char in str1

compare_loop:
        mov     ah,[ecx + 2]        ; put next char from str2 into ah
        test    ah,ah               ; end of str2?
        jz      match               ; if so, then a match has been found

        mov     al,[esi]            ; get next char from str1
        add     esi,2               ; bump pointer into str1 by 2

        cmp     al,ah               ; are chars from str1 and str2 equal?
        jne     findnext            ; no

; do one more iteration

        mov     al,[ecx + 3]        ; put the next char from str2 into al
        test    al,al               ; end of str2
        jz      match               ; if so, then a match has been found

        mov     ah,[esi - 1]        ; get next char from str1
        add     ecx,2               ; bump pointer in str1 by 2
        cmp     al,ah               ; are chars from str1 and str2 equal?
        je      compare_loop

; no match. test some more chars (to improve execution time for bad strings).

        jmp     findnext

; str2 string contains only one character so it's like the strchr functioin

strchr_call:
        xor     eax,eax
        pop     esi
        pop     ebx
        pop     edi
        mov     al,dl
        jmp     __from_strstr_to_strchr

;
;
; Match!  Return (ebx - 1)
;
match:
        lea     eax,[edi - 1]
        pop     esi
        pop     ebx
        pop     edi
        ret

empty_str2:           ; empty target string, return src (ANSI mandated)
        mov     eax,edi
        pop     esi
        pop     ebx
        pop     edi
        ret

strstr  endp
        end

어셈블리로 최적화된 strstr 함수 말고, C 언어로 된 strstr 함수로 테스트 하는 경우에는 Boyer-Moore 알고리즘이 대체로 빠릅니다.

52KB의 문자열을 준비하고 발견되지 않는 문자열로 10번씩 테스트 해보면,

// x86/Release 빌드
// (낮을 수록 빠릅니다.)
// boyer_moore 검색 시간
boyer_moore (861)
boyer_moore (881)
boyer_moore (979)
boyer_moore (849)
boyer_moore (847)
boyer_moore (865)
boyer_moore (870)
boyer_moore (912)
boyer_moore (853)
boyer_moore (850)

// strstr 검색 시간
strstr (1691)
strstr (1335)
strstr (1320)
strstr (1357)
strstr (1338)
strstr (1296)
strstr (1294)
strstr (1290)
strstr (1291)
strstr (1587)

boyer_moore가 빠릅니다. 1/3 지점 앞 부분에 나오는 약간 긴 텍스트로 검색해 보면,

// boyer_moore 검색 시간
boyer_moore (260)
boyer_moore (31)
boyer_moore (32)
boyer_moore (32)
boyer_moore (42)
boyer_moore (33)
boyer_moore (32)
boyer_moore (31)
boyer_moore (32)
boyer_moore (32)

// strstr 검색 시간
strstr (1345)
strstr (704)
strstr (1498)
strstr (1501)
strstr (403)
strstr (183)
strstr (184)
strstr (183)
strstr (182)
strstr (362)

역시 boyer_moore가 빠릅니다. 뒤에서 1/3 지점에 나오는 짧은 텍스트로 검색을 해보면,

boyer_moore (457)
boyer_moore (431)
boyer_moore (429)
boyer_moore (429)
boyer_moore (430)
boyer_moore (430)
boyer_moore (429)
boyer_moore (430)
boyer_moore (429)
boyer_moore (429)

strstr (213)
strstr (212)
strstr (212)
strstr (212)
strstr (211)
strstr (212)
strstr (340)
strstr (214)
strstr (213)
strstr (213)

이번만큼은 C 언어 함수인 strstr이 빠릅니다.

재미있군요. ^^ strstr C 함수가 사실 굉장히 간단한 편인데도 불구하고 assembly로 최적화했을 때의 속도 향상이 '월등'해지는 것이 꽤나 인상적입니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 2/6/2015]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...
NoWriterDateCnt.TitleFile(s)
12111정성태1/12/202020486디버깅 기술: 155. C# - KernelMemoryIO 드라이버를 이용해 실행 프로그램을 숨기는 방법(DKOM: Direct Kernel Object Modification) [16]파일 다운로드1
12110정성태1/11/202019816디버깅 기술: 154. Patch Guard로 인해 블루 스크린(BSOD)가 발생하는 사례 [5]파일 다운로드1
12109정성태1/10/202016570오류 유형: 588. Driver 프로젝트 빌드 오류 - Inf2Cat error -2: "Inf2Cat, signability test failed."
12108정성태1/10/202017404오류 유형: 587. Kernel Driver 시작 시 127(The specified procedure could not be found.) 오류 메시지 발생
12107정성태1/10/202018580.NET Framework: 877. C# - 프로세스의 모든 핸들을 열람 - 두 번째 이야기
12106정성태1/8/202019627VC++: 136. C++ - OSR Driver Loader와 같은 Legacy 커널 드라이버 설치 프로그램 제작 [1]
12105정성태1/8/202018136디버깅 기술: 153. C# - PEB를 조작해 로드된 DLL을 숨기는 방법
12104정성태1/7/202019328DDK: 9. 커널 메모리를 읽고 쓰는 NT Legacy driver와 C# 클라이언트 프로그램 [4]
12103정성태1/7/202022444DDK: 8. Visual Studio 2019 + WDK Legacy Driver 제작- Hello World 예제 [1]파일 다운로드2
12102정성태1/6/202018792디버깅 기술: 152. User 권한(Ring 3)의 프로그램에서 _ETHREAD 주소(및 커널 메모리를 읽을 수 있다면 _EPROCESS 주소) 구하는 방법
12101정성태1/5/202019039.NET Framework: 876. C# - PEB(Process Environment Block)를 통해 로드된 모듈 목록 열람
12100정성태1/3/202016537.NET Framework: 875. .NET 3.5 이하에서 IntPtr.Add 사용
12099정성태1/3/202019379디버깅 기술: 151. Windows 10 - Process Explorer로 확인한 Handle 정보를 windbg에서 조회 [1]
12098정성태1/2/202019118.NET Framework: 874. C# - 커널 구조체의 Offset 값을 하드 코딩하지 않고 사용하는 방법 [3]
12097정성태1/2/202017181디버깅 기술: 150. windbg - Wow64, x86, x64에서의 커널 구조체(예: TEB) 구조체 확인
12096정성태12/30/201919862디버깅 기술: 149. C# - DbgEng.dll을 이용한 간단한 디버거 제작 [1]
12095정성태12/27/201921569VC++: 135. C++ - string_view의 동작 방식
12094정성태12/26/201919298.NET Framework: 873. C# - 코드를 통해 PDB 심벌 파일 다운로드 방법
12093정성태12/26/201918871.NET Framework: 872. C# - 로딩된 Native DLL의 export 함수 목록 출력파일 다운로드1
12092정성태12/25/201917661디버깅 기술: 148. cdb.exe를 이용해 (ntdll.dll 등에 정의된) 커널 구조체 출력하는 방법
12091정성태12/25/201919957디버깅 기술: 147. pdb 파일을 다운로드하기 위한 symchk.exe 실행에 필요한 최소 파일 [1]
12090정성태12/24/201920065.NET Framework: 871. .NET AnyCPU로 빌드된 PE 헤더의 로딩 전/후 차이점 [1]파일 다운로드1
12089정성태12/23/201918993디버깅 기술: 146. gflags와 _CrtIsMemoryBlock을 이용한 Heap 메모리 손상 여부 체크
12088정성태12/23/201917961Linux: 28. Linux - 윈도우의 "Run as different user" 기능을 shell에서 실행하는 방법
12087정성태12/21/201918421디버깅 기술: 145. windbg/sos - Dictionary의 entries 배열 내용을 모두 덤프하는 방법 (do_hashtable.py) [1]
12086정성태12/20/201920939디버깅 기술: 144. windbg - Marshal.FreeHGlobal에서 발생한 덤프 분석 사례
... 61  62  63  64  65  66  67  68  69  70  71  72  [73]  74  75  ...