2013-05-06 3 views
4

내가 짧은 문자열 리터럴 문자열 다음 코드 예제에 설명 된 방법 비교 strcmp와의 호출에 필요한 오버 헤드를 원하지 않는 경우 :어떻게 문자 짧은 문자열로 문자열을 비교 효과적으로

#ifdef LITTLE_ENDIAN  //little-endian-addressing 
#define BytesAsDWord_M(a, b, c, d)\ 
    ((ulong) ((a) | ((b) << 8) | ((ulong) (c) << 16) | ((ulong) (d) << 24))) 

#define BytesAsWord_M(a, b)((ushort) ((a) | ((b) << 8))) 

#else //LITTLE_ENDIAN  //little-endian-addressing 
#define BytesAsDWord_M(a, b, c, d)\ 
((ulong) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))) 

#define BytesAsWord_M(a, b) ((ushort) ((b) | ((a) << 8))) 
#endif //LITTLE_ENDIAN  //little-endian-addressing 

bool AbsCompare(char* chr_p) 
//compare string with "abs" 
{ 
    if (*((ulong*) &chr_p[1]) == 
    BytesAsDWord_M('a', 'b', 's', '\0')) 
    return true; 

    return false; 
} 

gcc는 최적화 옵션을 사용하지 않고 컴파일하는 한이 예제를 컴파일합니다.

는 "엄격한 앨리어싱 규칙을 깰 것이다 타입 punned 포인터를 역 참조"

심지어 -O3로 최적화하는 것은 예 효과적 코드 발생하지 않습니다

가 보여 :

최적화 내가 경고를 활성화
//abstest.c 

#include <string.h> 

typedef unsigned long ulong; 
typedef unsigned short ushort; 

#if BYTE_ORDER == LITTLE_ENDIAN  //little-endian-addressing 
#define BytesAsDWord_M(a, b, c, d)\ 
    ((ulong) ((a) | ((b) << 8) | ((ulong) (c) << 16) | ((ulong) (d) << 24))) 

#define BytesAsWord_M(a, b)((ushort) ((a) | ((b) << 8))) 

#else //BYTE_ORDER == LITTLE_ENDIAN //little-endian-addressing 
#define BytesAsDWord_M(a, b, c, d)\ 
((ulong) ((d) | ((c) << 8) | ((b) << 16) | ((a) << 24))) 

#define BytesAsWord_M(a, b) ((ushort) ((b) | ((a) << 8))) 
#endif //BYTE_ORDER == LITTLE_ENDIAN //little-endian-addressing 

int AbsCompare1(char* chr_p) 
{ 
    return *(ulong*) chr_p == BytesAsDWord_M('a', 'b', 's', '\0'); 
} 

int AbsCompare2(char* chr_p) 
{ 
    return strcmp(chr_p, "abs"); 
} 

int main(int argc __attribute__((unused)), char ** argv) 
{ 
    int i; 
    int j; 

    i = AbsCompare1(argv[0]); 
    j = AbsCompare2(argv[0]); 

    return i + j; 
} 

objdump를 -d -Mintel abstest :

080483d0 <AbsCompare1>: 
80483d0: 55      push ebp 
80483d1: 89 e5     mov ebp,esp 
80483d3: 8b 45 08    mov eax,DWORD PTR [ebp+0x8] 
80483d6: 5d      pop ebp 
80483d7: 81 38 61 62 73 00  cmp DWORD PTR [eax],0x736261 
80483dd: 0f 94 c0    sete al 
80483e0: 0f b6 c0    movzx eax,al 
80483e3: c3      ret  

080483f0 <AbsCompare2>: 
80483f0: 55      push ebp 
80483f1: 0f b6 0d 5c 85 04 08 movzx ecx,BYTE PTR ds:0x804855c 
80483f8: 89 e5     mov ebp,esp 
80483fa: 8b 55 08    mov edx,DWORD PTR [ebp+0x8] 
80483fd: 0f b6 02    movzx eax,BYTE PTR [edx] 
8048400: 29 c8     sub eax,ecx 
8048402: 75 2b     jne 804842f <AbsCompare2+0x3f> 
8048404: 0f b6 42 01    movzx eax,BYTE PTR [edx+0x1] 
8048408: 0f b6 0d 5d 85 04 08 movzx ecx,BYTE PTR ds:0x804855d 
804840f: 29 c8     sub eax,ecx 
8048411: 75 1c     jne 804842f <AbsCompare2+0x3f> 
8048413: 0f b6 42 02    movzx eax,BYTE PTR [edx+0x2] 
8048417: 0f b6 0d 5e 85 04 08 movzx ecx,BYTE PTR ds:0x804855e 
804841e: 29 c8     sub eax,ecx 
8048420: 75 0d     jne 804842f <AbsCompare2+0x3f> 
8048422: 0f b6 42 03    movzx eax,BYTE PTR [edx+0x3] 
8048426: 0f b6 15 5f 85 04 08 movzx edx,BYTE PTR ds:0x804855f 
804842d: 29 d0     sub eax,edx 
804842f: 5d      pop ebp 
8048430: c3      ret  

직접이 짧은 문자를 비교하는 가능성이 특히 "& chr_p [1]"과 같은 임의의 인덱스에서 chr_p를 비교하기를 원하기 때문에 조합에 chr_p를 삽입하지 않아도됩니까?

+0

빅 엔디안 아키텍처와 리틀 엔디안 아키텍처를 모두 대상으로하고 있지만 정렬 문제는 고려하지 않은 것으로 나타났습니다. '& chr_p [1]'과 같은 표현식은 어떤 점에서 비 단어 정렬 주소로 거의 확실하게 평가 될 것입니다. –

+0

... 내가 아는 한 성능 제한 만있을 것입니다. – Wosh

+0

@Wosh 일부 아키텍처에서는 액세스가 잘못 정렬되어 하드웨어 예외가 발생합니다. –

답변

9

아니요. 컴파일러가 strcmp에 대한 지식을 사용한다는 사실을 알고 있습니까? 소스에서 타입 펀치에 의존하지 않고도 (호출 오버 헤드를 제거하여) 달성하고자하는 것을 정확히 수행합니다. 이러한 종류의 코드 변환은 컴파일러가 별칭 분석의 모든 이점을 얻은 후에 코드 생성기에서 가장 자주 수행됩니다.

을 사용하여 다음 프로그램을 컴파일하는 경우 strcmp을 호출 할 필요가 없습니다. 예를 들어

#include <string.h> 

int main(int argc, char ** argv) 
{ 
     return strcmp(argv[0], "abs"); 
} 

내 86 어셈블리이 (GCC 버전 4.3.2 (데비안 4.3.2-1.1)) (내가 나이 알고)처럼 보였다 :

main: 
     leal 4(%esp), %ecx 
     andl $-16, %esp 
     pushl -4(%ecx) 
     pushl %ebp 
     movl %esp, %ebp 
     pushl %ecx 
     movl 4(%ecx), %eax 
     movl (%eax), %edx 
     movzbl (%edx), %eax 
     subl $97, %eax 
     jne  .L2 
     movzbl 1(%edx), %eax 
     subl $98, %eax 
     jne  .L2 
     movzbl 2(%edx), %eax 
     subl $115, %eax 
     jne  .L2 
     movzbl 3(%edx), %eax 
.L2: 
     popl %ecx 
     popl %ebp 
     leal -4(%ecx), %esp 
     ret 

은 기본적으로 strcmp가 인라인되었으며, 펼쳤다. 물론 그것은 타겟의 코드 생성기에 크게 의존합니다. 아직 충분히 진화되지 않았다면 여전히 strcmp을 생성 할 수 있습니다. 코드 생성기가 나중에 이것을 지원할 것인가하는 경우 추한 코드로 스스로를 부담해야하는지 여전히 궁금하게 생각합니다. 코드가 여전히 복잡 할 때.

+0

다음 코드는 무엇입니까? 내'gcc -O3 -S'는'strcmp()'에 대한 꼬리 호출을 생성합니다. –

+0

@Pascal : 하나는'repeps cmpsb' 명령을 받는다. (수행자 설정과 마무리로). x86을 대상으로하는 GCC 4.7.2가 그 것이다. –

+0

그냥 테스트했는데, x86 용 GCC 4.7.2에서 "abs"의 각 문자에 대해 전개되지 않은 일련의'jne' 테스트가 있습니다. 흥미롭게도 C++과 같은 코드를 컴파일하면'repz cmpsb'가 생성됩니다. – SirDarius

관련 문제