Подтвердить что ты не робот

Strcmp для пустой строки

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

Ответ 3

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

if( !*str )

и

if( *str )

чтобы проверить непустую строку.

Ответ 4

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

Вы правы, что 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 в зависимости от контекста).

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

С 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] Хотя будьте осторожны - в случаях, когда более сложный, чем прямой тест против нуля, библиотека и код компилятора обычно будут лучше, чем ручной код.

Ответ 5

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

Ответ 6

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

Ответ 7

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

Ответ 8

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

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)