Diferencias de rendimiento al comparar símbolos y cadenas


7

En sx.el, tenemos un caso en el que necesitamos verificar si hemos aprobado GETo POSTcomo argumento.

Actualmente tenemos el argumento pasando como una cadena y lo estamos usando (string= "GET" url-method)para compararlo "GET".

¿Hay alguna ventaja de compilación elisp / byte para cambiarlo a un símbolo (equal url-method 'GET)?


Independientemente de la velocidad, los símbolos son idiomáticos para este propósito en Lisp. Además, utiliza eqpara comparar contra un símbolo.

eqconvertirá objetos de Emacs inty los comparará. Comparar ints es mucho más rápido que comparar strings.
abo-abo

1
string=funcionará como se espera, ya url-methodsea ​​un símbolo o una cadena. OTOH si usa eqo equal, debe asegurarse de que los argumentos sean del mismo tipo.
YoungFrog el

Respuestas:


11

Los nuevos usuarios de Lisp que vienen de otros idiomas a veces usan cadenas para tales fines por costumbre.

Los símbolos se pueden comparar con en eqlugar de solo equal. string=usa un código similar a equal, para probar cada carácter hasta que se encuentre una falta de coincidencia. Entonces sí, la comparación de símbolos puede ser un poco más rápida. Si está utilizando la comparación en un bucle, por ejemplo, la diferencia podría ser importante.

Además, dependiendo del código particular, a menudo puede hacer que el código sea más simple o más legible para usar símbolos en lugar de cadenas. Y hay funciones (y macros, etc.) que esperan símbolos o que comparan cosas usando eq(o eql) por defecto. Para usar aquellos con cadenas, de todos modos debe convertir a símbolos.


7

Tenga en cuenta que el lector lisp practica símbolos, de modo que las referencias independientes a un símbolo dado le dan exactamente el mismo objeto lisp , y en consecuencia puede compararlos eq(que solo necesita comparar las direcciones de los objetos).

Por el contrario, las cadenas independientes son siempre objetos lisp diferentes y, por lo tanto, debe comparar sus contenidos.

Por lo tanto, esperaría que la eqcomparación gane por rendimiento.

Curiosamente (ciertamente estoy muy sorprendido), algunas pruebas triviales benchmark-runestán dando string=la victoria por bastante margen. Esto me parece muy extraño. YMMV?


Editar: Así que acabo de notar esta respuesta (y su comentario) nuevamente, y me sentí inspirado para ver si podía recrear y explicar el resultado.

nb Nada se compila en bytes inicialmente.

La primera constatación fue que mis símbolos eran quoted y las cadenas no, e inmediatamente descubrí que quoteera el responsable de la mayor parte de la diferencia de velocidad:

(benchmark-run 10000000
  (string= "foo" "foo"))

es, para cadenas más pequeñas, consistentemente más rápido que:

(benchmark-run 10000000
  (eq 'foo 'foo))

sin embargo, si también citamos la cadena:

(benchmark-run 10000000
  (string= '"foo" '"foo"))

eso casi iguala las cosas por completo.

Sin embargo, en promedio, a menos que la cuerda sea bastante grande, la comparación de la cuerda todavía parece ganar por un pelo.

Un enfoque alternativo es vincular los objetos a las variables:

(let ((foo 'test)
      (bar 'test))
  (benchmark-run 10000000
    (eq foo bar)))

(let ((foo "test")
      (bar "test"))
  (benchmark-run 10000000
    (string= foo bar)))

Una vez más, estoy viendo la comparación de cuerdas más rápido por nariz, en promedio.

Después de la compilación de bytes, solo veo un resultado consistente para los casos variables de let-bound, donde eqes consistentemente más rápido que string=(toma alrededor de 2/3 del tiempo).

Para los otros ejemplos, obtengo resultados sin sentido (para mí) como que las cadenas entre comillas son más rápidas que las cadenas sin comillas, lo que solo puedo adivinar es efectivamente el ruido de otros aspectos de la ejecución y / o en benchmark-runsí mismo. Había suficiente variedad entre diferentes ejecuciones de la misma función para ocultar por completo cualquier diferencia entre ejecuciones de diferentes funciones.


Mis conclusiones son:

(a) las eqcomparaciones con un símbolo pueden (algo contraintuitivamente) ser más lentas que una comparación de cadenas, si se cita el símbolo.

(b) A menos que las cadenas sean bastante grandes, las diferencias prácticas son completamente insignificantes, por lo que no me molestaría en convertir una a la otra por razones puramente de rendimiento.


3
Posiblemente porque la forma en que configuró su prueba hace que cada símbolo se genere nuevamente en cada ejecución del ciclo, por lo que está enfrentando intern+ eqcontra string=. O posiblemente por alguna razón completamente diferente: es imposible saberlo sin ver su código. Debe publicar su código de prueba como una pregunta en este sitio.
Gilles 'SO- deja de ser malvado'

Intenté usar benchmark-run-compiled(con la idea de que eso eliminaría el costo de intern), pero varias veces obtuve resultados negativos. Creo que ambas operaciones son demasiado rápidas para medir de manera confiable.
npostavs
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.