strcmp для пустой строки - PullRequest
26 голосов
/ 01 июня 2011

Я просматривал какой-то код и увидел, что кто-то делает

if (0 == strcmp(foo,""))

Мне любопытно, потому что я думаю, что было бы быстрее сделать

if (foo[0] == '\0')

Это правильно илиstrcmp достаточно оптимизирован, чтобы сделать их одинаковыми.

(я понимаю, что даже если бы была какая-то разница, она была бы небольшой, но я думаю, что вы сохраните хотя бы несколько инструкций, используя мой метод.)

Ответы [ 8 ]

10 голосов
/ 01 июня 2011

Вы правы: так как вызов strcmp() добавляет управление стеком и скачок памяти к фактическим инструкциям strcmp, вы получите несколько инструкций, просто проверив первый байт вашей строки.

Для вашего любопытства вы можете проверить код strcmp () здесь: http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h=bd53c05c6e21130b091bd75c3fc93872dd71fe4b;hb=HEAD

(я думал, что код будет заполнен #ifdef и неясен __GNUSOMETHING, но на самом деле это довольно просто!)

9 голосов
/ 01 июня 2011

strcmp () является вызовом функции и, следовательно, имеет накладные расходы на вызов функции. foo [0] - это прямой доступ к массиву, поэтому он, очевидно, быстрее.

5 голосов
/ 01 июня 2011

Я не вижу преимущества использования strcmp в этом случае.Компилятор может быть достаточно умным, чтобы оптимизировать его, но это не будет быстрее, чем проверка байта '\ 0' напрямую.Исполнитель этого, возможно, выбрал эту конструкцию, потому что он думал, что она более читабельна, но я думаю, что это дело вкуса в этом случае.Хотя я бы написал этот чек немного по-другому, так как именно эту идиому чаще всего используют для проверки на наличие пустой строки:

if( !*str )

и

if( *str )

длянепустая строка.

4 голосов
/ 03 июня 2011

+ 1 к Gui13 за предоставление ссылки на источник gcc stdlib strcmp (http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h=bd53c05c6e21130b091bd75c3fc93872dd71fe4b;hb=HEAD)!

Вы правы, что strcmp никогда не может быть быстрее, чем прямое сравнение [1], но вопрос в том, будет ли компилятороптимизировать это? Я был напуган, чтобы попытаться измерить это, но я был приятно удивлен тем, насколько легко это было. Мой пример кода (без заголовков):

bool isEmpty(char * str) {
   return 0==std::strcmp(str,"");
}
bool isEmpty2(char * str) {
   return str[0]==0;
}

И я попытался скомпилировать это, сначала с gcc -S -o- emptystrcmptest.cc и затем с gcc -S -O2 -o- emptystrcmptest.cc. К моему приятному удивлению, хотя я не очень хорошо читаю сборку, неоптимизированная версия явно показала разницу, а оптимизированная версия ясно показала, что две функции создали одинаковую сборку.

Итак, я бы сказал, что в целом нет смысла беспокоиться об этом уровне оптимизации.

Если вы используете компилятор для встроенной системы и знаете, что он не справляется с такой простой оптимизацией (или вообще не иметь стандартной библиотеки), используйте специальную версию с ручным кодированием.

Если вы кодируете нормально, выВот более читаемая версия (imho, которая может быть strcmp или strlen или [0] == 0 в зависимости от контекста).

Если вы пишете высокоэффективный код, вы ожидаете, что его будут вызывать тысячи или миллионы раз в секунду, (a) тест, который на самом деле более эффективен, и (b) если читаемая версия на самом деле слишком медленная, попробуйте написать что-нибудь, что скомпилируется для лучшей сборки.

С gcc -S -o- emptystrcmptest.cc:

            .file   "emptystrcmptest.cc"
            .section .rdata,"dr"
    LC0:
            .ascii "\0"
            .text
            .align 2
    .globl __Z7isEmptyPc
            .def    __Z7isEmptyPc;  .scl    2;      .type   32;     .endef
    __Z7isEmptyPc:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $24, %esp
            movl    $LC0, 4(%esp)
            movl    8(%ebp), %eax
            movl    %eax, (%esp)
            call    _strcmp
            movl    %eax, -4(%ebp)
            cmpl    $0, -4(%ebp)
            sete    %al
            movzbl  %al, %eax
            movl    %eax, -4(%ebp)
            movl    -4(%ebp), %eax
            leave
            ret
            .align 2
    .globl __Z8isEmpty2Pc
            .def    __Z8isEmpty2Pc; .scl    2;      .type   32;     .endef
    __Z8isEmpty2Pc:
            pushl   %ebp
            movl    %esp, %ebp
            movl    8(%ebp), %eax
            cmpb    $0, (%eax)
            sete    %al
            movzbl  %al, %eax
            popl    %ebp
            ret
    emptystrcmptest.cc:10:2: warning: no newline at end of file
            .def    _strcmp;        .scl    2;      .type   32;     .endef

С gcc -S -O2 -o- emptystrcmptest.cc:

        .file   "emptystrcmptest.cc"
emptystrcmptest.cc:10:2: warning: no newline at end of file
        .text
        .align 2
        .p2align 4,,15
.globl __Z7isEmptyPc
        .def    __Z7isEmptyPc;  .scl    2;      .type   32;     .endef
__Z7isEmptyPc:
        pushl   %ebp
        movl    %esp, %ebp
        movl    8(%ebp), %eax
        popl    %ebp
        cmpb    $0, (%eax)
        sete    %al
        movzbl  %al, %eax
        ret
        .align 2
        .p2align 4,,15
.globl __Z8isEmpty2Pc
        .def    __Z8isEmpty2Pc; .scl    2;      .type   32;     .endef
__Z8isEmpty2Pc:
        pushl   %ebp
        movl    %esp, %ebp
        movl    8(%ebp), %eax
        popl    %ebp
        cmpb    $0, (%eax)
        sete    %al
        movzbl  %al, %eax
        ret

[1] Хотя будьте осторожны - в случаях, более сложных, чем прямой тест на ноль, код библиотеки и компилятора обычно будет лучше, чем ручнойкод.

1 голос
/ 01 июня 2011

Хороший оптимизирующий компилятор может оптимизировать вызов функции и затем исключить цикл из встроенной функции. Ваш метод не может быть медленнее, хотя есть вероятность, что он будет такой же скорости.

0 голосов
/ 01 июня 2011

Очевидно, что это будет быстрее, и, вероятно, стоит поместить свой собственный код во встроенную функцию или, возможно, даже в макрос, если вы планируете двигаться вперед с ним:

int isEmpty(const char *string)
{
    return ! *string;
}

int isNotEmpty(const char *string)
{
    return *string;
}

int isNullOrEmpty(const char *string)
{
    return string == NULL || ! *string;
}

int isNotNullOrEmpty(const char *string)
{
    return string != NULL && *string;
}

и дайтекомпилятор оптимизирует это для вас.В любом случае, strcmp необходимо в конечном итоге проверить '\0', поэтому вы всегда по крайней мере равны ему.(честно говоря, я бы, вероятно, позволил компилятору оптимизировать внутреннее совместное использование вышеперечисленного, например, isEmpty, вероятно, просто перевернул бы isNotEmpty)

0 голосов
/ 01 июня 2011

Это микрооптимизация настолько, насколько это возможно, но я полагаю, что если вы добавите нулевую проверку перед индексированием foo (если вы не знаете, что она никогда не будет нулевой), это технически сэкономит накладные расходы при вызове функции.

0 голосов
/ 01 июня 2011

Доступ к массиву - это порядок 1 во время выполнения, поэтому он быстрее функции.

...