2011-06-01 15 views
24

que estaba revisando algo de código y vi a alguien hacer unstrcmp para la cadena vacía

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

Tengo curiosidad porque creo que sería más rápido para hacer un

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

¿Es esto correcto o es strcmp optimizado lo suficiente como para hacerlos iguales.

(me di cuenta de que incluso si había alguna diferencia sería pequeña, pero estoy pensando a ahorrar al menos un par de instrucciones mediante el uso de mi método.)

+0

Tienes razón para cuestionar el patrón, y me gusta más tu forma de hacerlo. – SingleNegationElimination

+0

Al verificar la cadena vacía, tiene más sentido para mí hacer 'if (strlen (foo) == 0)'. –

+12

@Jamie Wong: si la cadena tiene 1Meg de longitud, entonces debe verificar 1 millón de bytes antes de encontrar el nulo de terminación; Esta es una pérdida bastante grande cuando solo está interesado en el caso en que la cadena tiene cero o más de cero caracteres. – SingleNegationElimination

Respuesta

10

Tiene razón: desde que llama a strcmp() suma la gestión de pila y el salto de memoria a las instrucciones de strcmp reales, obtendrá algunas instrucciones simplemente marcando el primer byte de su cadena.

Para su curiosidad, se puede comprobar el código de strcmp() aquí: (! Pensé que el código se llenaría de #ifdef y oscura __GNUSOMETHING, pero en realidad es bastante simple) http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h=bd53c05c6e21130b091bd75c3fc93872dd71fe4b;hb=HEAD

9

strcmp() es una llamada a la función y por lo tanto tiene una función de llamada de arriba. foo [0] es el acceso directo a la matriz, por lo que es obviamente más rápido.

0

acceso a una matriz es orden 1 en tiempo de ejecución, por lo tanto, es más rápido que la función.

0

Esto es tan micro optimizador como se puede, pero supongo que si agregó una comprobación nula antes de indexar foo (a menos que sepa que nunca será nulo) técnicamente se guardará la sobrecarga de una llamada a función.

+1

Creo que no entendió el punto. OP no está buscando un puntero nulo, sino una cadena de longitud cero. Nunca hay un propósito para realizar tanto la comprobación directa como la llamada 'strcmp' ya que los efectos son los mismos. –

+1

@R Creo que deberías volver a leer mi respuesta. Simplemente estaba diciendo que debería realizar una comprobación nula de un puntero antes de indexarlo ciegamente. No dije nada sobre llamar a strcmp. –

5

No veo ninguna ventaja de usar strcmp en este caso. El compilador debe ser lo suficientemente inteligente como para optimizarlo, pero no será más rápido que buscar el byte '\ 0' directamente. El implementador de esto podría haber elegido este constructo porque pensó que era más legible pero creo que esto es una cuestión de gusto en este caso. Aunque me gustaría escribir el cheque un poco diferente, ya que es el idioma que parece que se usa más a menudo para comprobar si hay una cadena vacía:

if(!*str) 

y

if(*str) 

para comprobar si hay una cadena no vacía .

+0

Sí, me gusta más, donde trabajo a través de pautas estrictas acerca de ser explícito en el condicional ... así que incluso si no tiene mucho sentido, tengo que comprobar explícitamente '\ 0' o 0 – Pablitorun

0

Es claramente va a ser más rápido, y probablemente vale la pena realizar su propio código en una función en línea, o tal vez incluso una macro, si piensa en seguir adelante con ella:

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; 
} 

y dejar que el compilador optimizar eso para ti. De todos modos, strcmp necesita verificar eventualmente '\0' para que siempre sea al menos igual a eso. (Honestamente, probablemente dejaría que el compilador optimizar el intercambio interno de los anteriores, por ejemplo, isEmpty probablemente sólo tapa isNotEmpty)

+0

Estoy bastante seguro de que la determinación de 'f() ==! A()' está más allá de las capacidades de cualquier optimizador. – mikerobi

+0

@mike Correcto, pero aún puede optimizar la llamada para estar en línea en lugar de presionar el mismo puntero en la pila. Esto también evita que el código potencialmente "no estándar" ensucie la base de código. – pickypg

1

Un buen compilador de optimización podría optimizar la distancia de la llamada a la función y luego eliminar el bucle de la función inline. No hay forma de que tu método sea más lento, aunque existe la posibilidad de que tenga la misma velocidad.

4

+1 a Gui13 por proporcionar un enlace a la fuente de gcc stdlib strcmp (http://sourceware.org/git/?p=glibc.git;a=blob;f=string/strcmp.c;h = bd53c05c6e21130b091bd75c3fc93872dd71fe4b; hb = HEAD)!

Tiene la certeza de que strcmp nunca puede ser más rápido que una comparación directa [1], pero la pregunta es, ¿el compilador lo optimizará? Me intimidaba intentar medir eso, pero me sorprendió gratamente lo fácil que era. Mi código de ejemplo es (omitiendo los encabezados):

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

y yo tratamos de compilar que, en primer lugar con gcc -S -o- emptystrcmptest.cc y luego con gcc -S -O2 -o- emptystrcmptest.cc. Para mi agradable sorpresa, aunque no puedo leer muy bien el ensamblaje, la versión no optimizada mostró claramente una diferencia, y la versión optimizada mostró claramente que las dos funciones tenían un ensamblaje idéntico.

Por lo tanto, yo diría que, en general, no tiene sentido preocuparse por este nivel de optimización.

Si está utilizando un compilador para un sistema integrado y sabe que no maneja este tipo de optimización simple (o no tiene una biblioteca estándar), use la versión de caso especial codificada a mano.

Si está codificando normalmente, use la versión más legible (lo que puede ser strcmp o strlen o [0] == 0 según el contexto).

Si está escribiendo código altamente eficiente, espera que lo llamen miles o millones de veces por segundo, (a) prueba que es realmente más eficiente y (b) si la versión legible es muy lenta, intente escribir algo que se compilará para un mejor ensamblaje.

Con 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 

Con 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] A pesar de tener cuidado - en los casos más complicados que una prueba directa contra cero, la biblioteca y el código de compilador de una típicamente SERÁ mejor que el código hecho a mano.

+0

+1 @Jack Esto le da una gran credibilidad al comentario de @Brandon Moretz. Buen post. Todavía no estoy seguro de cuál prefiero en términos de legibilidad pura, pero definitivamente este es un buen punto en términos de optimizaciones prematuras. – pickypg