2013-05-06 8 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 компилирует этот пример, пока я компилирую без включенных опций оптимизации. С оптимизацией включен я получаю предупреждение:

«разыменования указателя типа-каламбурил будет нарушать правила строгого сглаживания»

Даже оптимизации с -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 в объединение, особенно потому, что я хочу сравнить chr_p при произвольных индексах типа «& chr_p [1]»?

+0

Я вижу, что вы ориентируетесь как на архитектуры большого, так и на маленького конца, но не учитываете возможные проблемы с выравниванием. Выражение типа '& chr_p [1]' почти наверняка будет оцениваться в некорректном адресе, отличном от слова. –

+0

... что бы я знал только по ограничению производительности. – Wosh

+0

@Wosh На некоторых архитектурах неправильный доступ приведет к аппаратным исключениям. –

ответ

9

Нет, нет. Знаете ли вы, что компилятор будет использовать свои знания о strcmp? Он будет делать то, что вам нравится (удаление служебных служебных обязанностей), не прибегая к типу пиннинга в источнике. Такие преобразования кода чаще всего выполняются в генераторе кода после того, как компилятор извлек все выгоды из анализа псевдонимов.

Если я использую gcc -O3 для компиляции следующей программы, нет никакого звонка на strcmp.

#include <string.h> 

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

Моя x86 сборка, например, выглядел так (GCC версии 4.3.2 (Debian 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: Я, например, получаю инструкцию 'repeat cmpsb' (с соответствующей настройкой и завершением). Это с GCC 4.7.2 таргетинга x86. –

+0

Просто протестировал это, получил развернутую серию тестов 'jne', для каждого символа« abs »с GCC 4.7.2 для x86. Интересно, что компиляция того же кода, что и C++, привела к 'repz cmpsb'. – SirDarius

Смежные вопросы