2009-05-07 13 views
5

¿Cuánto más lento puedo razonablemente esperar que sea perform: que enviar un mensaje literal, en promedio? ¿Debo evitar enviar perform: en un bucle, similar a la advertencia dada a los programadores de Perl/Python para evitar llamar al eval("...") (Compiler evaluate: en Smalltalk) en un bucle?Rendimiento de rendimiento del rendimiento: en Smalltalk (específicamente Squeak)

Me preocupa principalmente Squeak, pero también estoy interesado en otros Smalltalks. Además, ¿la sobrecarga es mayor con las variantes perform:with:? Gracias

Respuesta

8

#perform: no es como eval(). El problema con eval() (en lo que respecta al rendimiento, de todos modos) es que tiene que compilar el código que está enviando en tiempo de ejecución, que es una operación muy lenta. Smalltalk's #perform:, por otro lado, es equivalente a Ruby's send() u Objective-C's performSelector: (de hecho, ambos idiomas fueron fuertemente inspirados por Smalltalk). Idiomas como estos ya buscan métodos basados ​​en su nombre: #perform:, solo le permite especificar el nombre en tiempo de ejecución en lugar de escribir. No tiene que analizar ninguna sintaxis ni compilar nada como eval().

Será un poco más lento (el costo de una llamada al método adicional al menos), pero no es como eval(). Además, las variantes con más argumentos no deberían mostrar ninguna diferencia en la velocidad frente a simplemente perform:whatever. No puedo hablar con mucha experiencia sobre Squeak específicamente, pero así es como generalmente funciona.

2

Éstos son algunos números de mi máquina (es Smalltalk/X, pero supongo que los números son comparables - al menos las proporciones debe ser):

Los llamados métodos "foo" y "foo:" son un noops (es decir, consisten en un^self):

self foo        ... 3.2 ns 
self perform:#foo      ... 3.3 ns 
[self foo] value      ... 12.5 ns (2 sends and 2 contexts) 
[ ] value        ... 3.1 ns (empty block) 
Compiler valuate:('TestClass foo')  ... 1.15 ms 

self foo:123       ... 3.3 ns 
self perform:#foo: with:123   ... 3.6 ns 
[self foo:123] value     ... 15 ns (2 sends and 2 contexts) 
[self foo:arg] value:123    ... 23 ns (2 sends and 2 contexts) 
Compiler valuate:('TestClass foo:123') ... 1.16 ms 

Aviso la gran diferencia entre "realizar:" y "evaluar:"; evaluar es llamar al compilador para analizar la cadena, generar un método de descarte (bytecode), ejecutarlo (se juntó en la primera llamada) y finalmente descartarlo. El compilador está escrito para ser utilizado principalmente para el IDE y para el código fileIn de flujos externos; tiene código para informes de errores, mensajes de advertencia, etc. En general, eval no es lo que desea cuando el rendimiento es crítico.

Tiempos de un Dell Vostro; su kilometraje puede variar, pero las proporciones no. Traté de obtener los tiempos de ejecución netos, midiendo el tiempo de bucle vacío y restando; también, ejecuté las pruebas 10 veces y tomé los mejores tiempos para eliminar OS/network/disk/email o cualquier otra perturbación. Sin embargo, realmente no me importaba una máquina sin carga. El código de medida fue (sustituyó la segunda timesRepeat-ARG con la materia anterior):

callFoo2 
    |t1 t2| 

    t1 := 
     TimeDuration toRun:[ 
      100000000 timesRepeat:[] 
     ]. 

    t2 := 
     TimeDuration toRun:[ 
      100000000 timesRepeat:[self foo:123] 
     ]. 

    Transcript showCR:t2-t1 

EDIT: PS: me olvidó mencionar: estos son los tiempos desde el IDE (ejecución es decir bytecode-compilados JIT) . El código estáticamente compilado (usando el compilador stc) generalmente será un poco más rápido (20-30%) en estos micro puntos de referencia de bajo nivel, debido a un mejor algoritmo de asignación de registros.

EDIT: intenté reproducir estos números el otro día, pero obtuve resultados completamente diferentes (8ns para la llamada simple, pero 9ns para la ejecución). Así que tenga mucho cuidado con estos micromundos, ya que se ejecutan completamente fuera de la memoria caché de primer nivel (y los mensajes vacíos incluso omiten la configuración de contexto, o ingresan), generalmente no son muy representativos del rendimiento general.