performance - ¿Cuáles son los principales bateadores de rendimiento en AS3 además de renderizar vectores?




actionscript-3 flash optimization (2)

En ActionScript 3, el uso de gráficos vectoriales es una forma garantizada de causar daños masivos al rendimiento de su proyecto.

Usar un solo Bitmap de Bitmap para todos los gráficos usando .copyPixels() través de su objeto BitmapData en lugar de todos los gráficos vectoriales generará un aumento de rendimiento ridículo y es esencial para que personas como yo desarrollen juegos dentro de Flash.

Más allá de esto, no estoy seguro de cuáles son las siguientes cosas importantes que debería enfocar e intentar optimizar. Utilizo muchas de las funciones de trigonometría incorporadas, pero no parecen afectarlo demasiado. Sé que hay algunas bibliotecas que optimizan las matemáticas con métodos de aproximación y similares, pero hasta ahora no las he encontrado necesarias.

¿Hay algún otro punto conocido masivo que debería estar mirando? Me refiero más a las cosas integradas de las que debería tener cuidado (como evitar la representación vectorial) en lugar de cómo mejorar mi propio estilo de codificación.


Answers

Los documentos que he encontrado útiles son:

Algunos aspectos destacados:

Elija los objetos de visualización apropiados

Uno de los consejos de optimización más simples para limitar el uso de la memoria es usar el tipo apropiado de objeto de visualización . Para formas simples que no son interactivas, usa objetos Shape . Para objetos interactivos que no necesitan una línea de tiempo, usa objetos Sprite . Para la animación que utiliza una línea de tiempo, use objetos MovieClip .

Use getSize() para comparar el código

getSize() devuelve el tamaño en la memoria de un objeto especificado.

Elija los tipos primitivos apropiados para conservar la memoria

Todos los tipos primitivos excepto String usan 4 - 8 bytes en la memoria. Un Number , que representa un valor de 64 bits, tiene 8 bytes asignados por la Máquina Virtual de ActionScript (AVM), si no tiene asignado un valor. El comportamiento difiere para el tipo de Cadena. Código de referencia y determinar el objeto más eficiente para la tarea.

Reutilizar objetos

Optimice la memoria mediante la reutilización de objetos y evite volver a crearlos siempre que sea posible.

Usar agrupación de objetos

La reutilización de objetos reduce la necesidad de crear instancias de objetos, lo que puede ser costoso. También reduce las posibilidades de que el recolector de basura se ejecute, lo que puede ralentizar su aplicación.

Memoria libre

Para asegurarse de que un objeto sea basura , elimine todas las referencias al objeto. La asignación de memoria, en lugar de la eliminación de objetos, desencadena la recolección de elementos no utilizados. Intente limitar los pases de recolección de basura reutilizando los objetos tanto como sea posible. Además, establezca referencias a nulo, cuando sea posible, para que el recolector de elementos no utilizados pase menos tiempo buscando los objetos. Piense en la recolección de basura como un seguro y siempre administre los tiempos de vida de los objetos explícitamente, cuando sea posible.

Establecer una referencia a un objeto de visualización como nulo no garantiza que el objeto esté congelado. El objeto continúa consumiendo ciclos de CPU hasta que sea basura recolectada.

BitmapData clase BitmapData incluye un método dispose() , aunque el método dispose elimina los píxeles de la memoria, la referencia todavía debe establecerse en null para liberarla por completo.

Usa bitmaps

El uso de vectores, especialmente en grandes cantidades, aumenta drásticamente la necesidad de recursos de CPU o GPU. El uso de bitmaps es una buena forma de optimizar el procesamiento, ya que el tiempo de ejecución necesita menos recursos de procesamiento para dibujar píxeles en la pantalla que para representar el contenido vectorial.

Evite los filtros, incluidos los filtros procesados ​​a través de Pixel Bender

Cuando se aplica un filtro a un objeto de visualización, el tiempo de ejecución crea dos mapas de bits en la memoria. El uso de mapas de bits creados externamente ayuda al tiempo de ejecución a reducir la carga de la CPU o GPU.

Use mipmapping para escalar imágenes grandes

Use mipmapping con moderación. Aunque mejora la calidad de los mapas de bits reducidos, tiene un impacto en el ancho de banda, la memoria y la velocidad.

Use Text Engine para texto de solo lectura, TextField para texto de entrada

Para texto de solo lectura, es mejor utilizar Flash Text Engine , que ofrece un bajo uso de memoria y una mejor representación. Para el texto de entrada, los objetos TextField son una mejor opción, porque se requiere menos código ActionScript para crear comportamientos típicos, como el manejo de entrada y el ajuste de palabras.

Use devoluciones de llamada en lugar de eventos

El uso del modelo de evento nativo puede ser más lento y consumir más memoria que usar una función de devolución de llamada tradicional. Los objetos de evento se deben crear y asignar en la memoria, lo que crea una disminución del rendimiento. Por ejemplo, al escuchar el evento Event.ENTER_FRAME , se crea un nuevo objeto de evento en cada cuadro para el controlador de eventos. El rendimiento puede ser especialmente lento para los objetos de visualización, debido a las fases de captura y burbujeo, que pueden ser costosas si la lista de visualización es compleja.

Congelar y descongelar objetos en agregado / eliminado del escenario

Incluso si los objetos de visualización ya no están en la lista de visualización y están esperando ser recogidos basura, aún podrían estar usando código intensivo de CPU.

El concepto de congelación también es importante cuando se carga contenido remoto con la clase Loader.

unloadAndStop() método unloadAndStop() permite descargar un archivo SWF, congelar automáticamente cada objeto en el archivo SWF cargado y forzar la ejecución del recolector de elementos no utilizados.

Utilice eventos Event.ACTIVATE y Event.DEACTIVATE para detectar inactividad de fondo

Event.ACTIVATE eventos Event.ACTIVATE y Event.DEACTIVATE permiten detectar cuándo el tiempo de ejecución gana o pierde el foco. Como resultado, el código se puede optimizar para reaccionar a los cambios de contexto.

Los eventos de activación y desactivación le permiten implementar un mecanismo similar a la función "Pausar y reanudar" que a veces se encuentra en dispositivos móviles y netbooks.

Deshabilitar la interacción del mouse cuando sea posible

La detección de la interacción del mouse puede consumir mucha CPU cuando se muestran muchos objetos interactivos en la pantalla, especialmente si se superponen. Cuando sea posible, considere deshabilitar la interacción del mouse, que ayuda a su aplicación a usar menos procesamiento de la CPU y, como resultado, a reducir el uso de la batería en los dispositivos móviles.

Usa temporizadores para contenido no animado

Los temporizadores son preferibles a los eventos Event.ENTER_FRAME para contenido no animado que se ejecuta durante mucho tiempo.

Un temporizador puede comportarse de forma similar a un evento Event.ENTER_FRAME , pero un evento puede enviarse sin estar vinculado a la velocidad de fotogramas. Este comportamiento puede ofrecer una optimización significativa. Considere una aplicación de reproductor de video como un ejemplo. En este caso, no necesita usar una velocidad de cuadros alta, ya que solo los controles de la aplicación se están moviendo.

Limite la interpolación

Limite el uso de interpolación, lo que ahorra procesamiento de CPU, memoria y duración de la batería, lo que ayuda a que el contenido se ejecute más rápido en dispositivos de nivel bajo.

Usar Vector vs. Array

La clase Vector permite un acceso de lectura y escritura más rápido que la clase Array .

El acceso e iteración del elemento de matriz son mucho más rápidos cuando se usa una instancia de Vector que cuando se usa una matriz.

En modo estricto, el compilador puede identificar los errores de tipo de datos.

La comprobación del rango de tiempo de ejecución (o la comprobación de longitud fija) aumenta la fiabilidad de forma significativa en Arrays.

Use la API de dibujo para una ejecución de código más rápida

Reduzca la cantidad de ejecución del código usando drawPath() , drawGraphicsData() , drawTriangles() Menos líneas de código pueden proporcionar un mejor rendimiento de ejecución de ActionScript.

Utilice la captura de eventos y el burbujeo para minimizar los manejadores de eventos

Aprovechar el burbujeo de un evento puede ayudarlo a optimizar el tiempo de ejecución del código ActionScript. Puede registrar un controlador de eventos en un objeto, en lugar de múltiples objetos, para mejorar el rendimiento.

Pinte los píxeles usando el método setVector()

Al pintar píxeles, se pueden realizar algunas optimizaciones simplemente usando los métodos apropiados de la clase BitmapData. Una forma rápida de pintar píxeles es usar el método setVector() .

lock() y unlock() BitmapData cuando se usan métodos lentos como setPixel() o setPixel32()

Llamar a lock() y unlock() evita que la pantalla se actualice innecesariamente. Es probable que los métodos que se repiten sobre píxeles, como getPixel() , getPixel32() , setPixel() y setPixel32() , sean lentos, especialmente en dispositivos móviles. Si es posible, use métodos que recuperen todos los píxeles en una llamada. Para leer píxeles, use el método getVector() , que es más rápido que el getPixels() . Además, recuerde usar API que se basan en objetos Vector, cuando sea posible, ya que es probable que se ejecuten más rápido.

Use métodos de clase String lugar de expresiones regulares

Cuando un método de clase String está disponible, se ejecuta más rápido que la expresión regular equivalente y no requiere la creación de otro objeto.

Para TextFields, use apendText() lugar del operador +=

El uso del método appendText() proporciona mejoras de rendimiento.

El operador de corchete cuadrado [] puede ralentizar el rendimiento: almacenar una referencia en una variable local

Usar el operador de corchetes puede ralentizar el rendimiento. Puede evitar usarlo almacenando su referencia en una variable local.

Reduce el número de llamadas de función moviendo el código en línea

Las funciones de llamada pueden ser costosas. Intente reducir el número de llamadas de función moviendo el código en línea.

Mover la llamada de función en línea produce un código que es más de cuatro veces más rápido.

Evite colocar el contenido fuera del escenario

Incluso si los elementos fuera del escenario no se muestran en la pantalla y no se representan, aún existen en la lista de visualización. El tiempo de ejecución continúa ejecutando pruebas internas en estos elementos para asegurarse de que todavía están fuera del escenario y el usuario no está interactuando con ellos.

Evite usar propiedad alpha

Cuando un objeto de visualización utiliza fusión alpha , el tiempo de ejecución debe combinar los valores de color de cada objeto de visualización apilado y el color de fondo para determinar el color final. Por lo tanto, la mezcla alfa puede ser más intensiva en el procesador que dibujar un color opaco. Este cálculo adicional puede dañar el rendimiento en dispositivos lentos.

Utilice la velocidad de fotogramas más baja posible

Una velocidad de cuadro más alta gasta más ciclos de CPU y energía de la batería que una velocidad más baja.

Fundamentos de ejecución del código de tiempo de ejecución

Utilice el almacenamiento en caché de mapas de bits para contenido vectorial complejo

Esta característica almacena en caché un objeto vectorial, lo renderiza como un mapa de bits internamente, y usa ese mapa de bits para renderizar. El almacenamiento en caché de mapa de bits mejora la representación si el contenido almacenado en la memoria caché no se rota, no se modifica ni se modifica en cada fotograma. Cualquier transformación que no sea la traslación en los ejes xey, la representación no se mejora.

Establecer la propiedad cacheAsBitmapMatrix cuando se utilizan mapas de bits en caché en aplicaciones de AIR para dispositivos móviles

cacheAsBitmapMatrix en el perfil móvil de AIR puede aplicar cualquier transformación bidimensional al objeto sin regenerar el mapa de bits en caché. También puede cambiar la propiedad alfa sin regenerar el mapa de bits en caché.

Utilice la clase BitmapData para crear un comportamiento de caché de mapa de bits personalizado

El uso de un solo mapa de bits almacenado en caché se usa en la memoria y lo comparten todas las instancias.

Aislar eventos como Event.ENTER_FRAME en un único controlador

Esta técnica ahorra recursos de CPU.

Utilice la función de almacenamiento en caché de mapa de bits y la propiedad opaqueBackground para mejorar el rendimiento del procesamiento de texto

La función de caché de mapa de bits le permite almacenar en caché contenido vectorial como mapas de bits para mejorar el rendimiento de la representación. Esta característica es útil para contenido vectorial complejo y también cuando se utiliza con contenido de texto que requiere que se procese el procesamiento.

La transparencia alfa impone una carga adicional al tiempo de ejecución al dibujar imágenes de mapa de bits transparentes. Puede usar la propiedad opaqueBackground para eludir eso, al especificar un color como fondo.

Habilitar aceleración de gráficos de hardware GPU

Para aprovechar la aceleración de la GPU del contenido de Flash con AIR para plataformas móviles, Adobe recomienda utilizar renderMode="direct" (es decir, Stage3D) en lugar de renderMode = "gpu". Adobe oficialmente admite y recomienda los siguientes frameworks basados ​​en Stage3D : Starling (2D) y Away3D (3D) .

Evite usar wmode = transparent o wmode = opaque en los parámetros de inserción de HTML. Estos modos pueden provocar una disminución del rendimiento. También pueden provocar una pequeña pérdida en la sincronización de audio y video tanto en la representación del software como del hardware. Además, muchas plataformas no son compatibles con el procesamiento de GPU cuando estos modos están en vigencia, lo que perjudica significativamente el rendimiento.

Favorecer el uso de versiones asincrónicas de las operaciones

El código de la aplicación en el hilo de ejecución actual continúa ejecutándose.

Las operaciones asincrónicas se programan y se dividen para evitar problemas de representación. En consecuencia, es mucho más fácil tener una aplicación receptiva que use versiones asíncronas de operaciones. Consulte el rendimiento percibido versus el rendimiento real para obtener más información.

Formas suaves para mejorar la representación

A diferencia de los mapas de bits, renderizar el contenido vectorial requiere muchos cálculos, especialmente para degradados y rutas complejas que contienen muchos puntos de control. Como diseñador o desarrollador, asegúrese de que las formas se optimicen lo suficiente.

Guarde en caché los activos localmente después de cargarlos, en lugar de cargarlos desde la red cada vez que se necesiten

Si su aplicación carga activos tales como medios o datos, guarde en caché los activos guardándolos en el dispositivo local. Para los activos que cambian con poca frecuencia, considere actualizar el caché a intervalos.

Utilice la clase StageVideo para aprovechar la aceleración de hardware

Use la clase StageVideo para aprovechar la aceleración de hardware para presentar video.

Este enfoque aprovecha al máximo el hardware de video subyacente. El resultado es una carga mucho menor en la CPU, lo que se traduce en mayores tasas de cuadros en dispositivos menos potentes y también menos uso de memoria.

El formato de audio AAC ofrece una mejor calidad y un tamaño de archivo más pequeño que el formato mp3 a una velocidad de bits equivalente

Similar a la decodificación de video, la decodificación de audio requiere altos ciclos de CPU y puede optimizarse aprovechando el hardware disponible en el dispositivo.

El formato AAC ofrece una mejor calidad y un tamaño de archivo más pequeño que el formato mp3 a una tasa de bits equivalente.

Minimizar código en constructores

La función de inicialización como los constructores se interpretan, todo lo demás es JIT.


Culpable: Dependencia de datos falsos (y el compilador ni siquiera lo sabe)

En los procesadores Sandy / Ivy Bridge y Haswell, la instrucción:

popcnt  src, dest

Parece tener una dependencia falsa en el destino del registro de destino. A pesar de que la instrucción solo le escribe, la instrucción esperará hasta que el dest esté listo antes de ejecutarse.

Esta dependencia no solo popcnt los 4 popcnt s de una única iteración de bucle. Puede transmitir iteraciones de bucle, lo que hace imposible que el procesador paralice diferentes iteraciones de bucle.

El unsigned vs. uint64_t y otros ajustes no afectan directamente el problema. Pero influyen en el asignador de registros que asigna los registros a las variables.

En su caso, las velocidades son un resultado directo de lo que está atascado en la cadena de dependencia (falsa) en función de lo que el asignador de registros decidió hacer.

  • 13 GB / s tiene una cadena: popcnt - add - popcnt - popcnt → siguiente iteración
  • 15 GB / s tiene una cadena: popcnt - add - popcnt - add → siguiente iteración
  • 20 GB / s tiene una cadena: popcnt - popcnt → siguiente iteración
  • 26 GB / s tiene una cadena: popcnt - popcnt → siguiente iteración

La diferencia entre 20 GB / sy 26 GB / s parece ser un artefacto menor del direccionamiento indirecto. De cualquier manera, el procesador comienza a golpear otros cuellos de botella una vez que alcanza esta velocidad.

Para probar esto, usé el ensamblaje en línea para omitir el compilador y obtener exactamente el ensamblaje que quiero. También divido la variable de count para romper todas las demás dependencias que puedan alterar los puntos de referencia.

Aquí están los resultados:

Sandy Bridge Xeon @ 3.5 GHz: (el código de prueba completo se puede encontrar en la parte inferior)

  • GCC 4.6.3: g++ popcnt.cpp -std=c++0x -O3 -save-temps -march=native
  • Ubuntu 12

Registros diferentes: 18.6195 GB / s

.L4:
    movq    (%rbx,%rax,8), %r8
    movq    8(%rbx,%rax,8), %r9
    movq    16(%rbx,%rax,8), %r10
    movq    24(%rbx,%rax,8), %r11
    addq    $4, %rax

    popcnt %r8, %r8
    add    %r8, %rdx
    popcnt %r9, %r9
    add    %r9, %rcx
    popcnt %r10, %r10
    add    %r10, %rdi
    popcnt %r11, %r11
    add    %r11, %rsi

    cmpq    $131072, %rax
    jne .L4

Mismo registro: 8.49272 GB / s

.L9:
    movq    (%rbx,%rdx,8), %r9
    movq    8(%rbx,%rdx,8), %r10
    movq    16(%rbx,%rdx,8), %r11
    movq    24(%rbx,%rdx,8), %rbp
    addq    $4, %rdx

    # This time reuse "rax" for all the popcnts.
    popcnt %r9, %rax
    add    %rax, %rcx
    popcnt %r10, %rax
    add    %rax, %rsi
    popcnt %r11, %rax
    add    %rax, %r8
    popcnt %rbp, %rax
    add    %rax, %rdi

    cmpq    $131072, %rdx
    jne .L9

Mismo registro con cadena rota: 17.8869 GB / s

.L14:
    movq    (%rbx,%rdx,8), %r9
    movq    8(%rbx,%rdx,8), %r10
    movq    16(%rbx,%rdx,8), %r11
    movq    24(%rbx,%rdx,8), %rbp
    addq    $4, %rdx

    # Reuse "rax" for all the popcnts.
    xor    %rax, %rax    # Break the cross-iteration dependency by zeroing "rax".
    popcnt %r9, %rax
    add    %rax, %rcx
    popcnt %r10, %rax
    add    %rax, %rsi
    popcnt %r11, %rax
    add    %rax, %r8
    popcnt %rbp, %rax
    add    %rax, %rdi

    cmpq    $131072, %rdx
    jne .L14

Entonces, ¿qué salió mal con el compilador?

Parece que ni GCC ni Visual Studio son conscientes de que popcnt tiene una dependencia tan falsa. Sin embargo, estas dependencias falsas no son infrecuentes. Es solo una cuestión de si el compilador es consciente de ello.

popcnt no es exactamente la instrucción más utilizada. Entonces, no es realmente una sorpresa que un compilador importante se pierda algo como esto. Tampoco parece haber documentación en ninguna parte que mencione este problema. Si Intel no lo revela, nadie lo sabrá hasta que alguien lo encuentre por casualidad.

( Actualización: a partir de la versión 4.9.2 , GCC está al tanto de esta dependencia falsa y genera un código para compensarlo cuando las optimizaciones están habilitadas. Los compiladores principales de otros proveedores, incluidos Clang, MSVC e incluso el propio ICC de Intel aún no lo saben este error de microarquitectura y no emitirá código que lo compense.)

¿Por qué la CPU tiene una dependencia tan falsa?

Solo podemos especular, pero es probable que Intel tenga el mismo manejo para muchas instrucciones de dos operandos. Instrucciones comunes como add , sub tomar dos operandos, ambos de los cuales son entradas. Por lo tanto, Intel probablemente popcnt a popcnt en la misma categoría para mantener el diseño del procesador simple.

Los procesadores de AMD no parecen tener esta dependencia falsa.

El código de prueba completo se encuentra a continuación como referencia:

#include <iostream>
#include <chrono>
#include <x86intrin.h>

int main(int argc, char* argv[]) {

   using namespace std;
   uint64_t size=1<<20;

   uint64_t* buffer = new uint64_t[size/8];
   char* charbuffer=reinterpret_cast<char*>(buffer);
   for (unsigned i=0;i<size;++i) charbuffer[i]=rand()%256;

   uint64_t count,duration;
   chrono::time_point<chrono::system_clock> startP,endP;
   {
      uint64_t c0 = 0;
      uint64_t c1 = 0;
      uint64_t c2 = 0;
      uint64_t c3 = 0;
      startP = chrono::system_clock::now();
      for( unsigned k = 0; k < 10000; k++){
         for (uint64_t i=0;i<size/8;i+=4) {
            uint64_t r0 = buffer[i + 0];
            uint64_t r1 = buffer[i + 1];
            uint64_t r2 = buffer[i + 2];
            uint64_t r3 = buffer[i + 3];
            __asm__(
                "popcnt %4, %4  \n\t"
                "add %4, %0     \n\t"
                "popcnt %5, %5  \n\t"
                "add %5, %1     \n\t"
                "popcnt %6, %6  \n\t"
                "add %6, %2     \n\t"
                "popcnt %7, %7  \n\t"
                "add %7, %3     \n\t"
                : "+r" (c0), "+r" (c1), "+r" (c2), "+r" (c3)
                : "r"  (r0), "r"  (r1), "r"  (r2), "r"  (r3)
            );
         }
      }
      count = c0 + c1 + c2 + c3;
      endP = chrono::system_clock::now();
      duration=chrono::duration_cast<std::chrono::nanoseconds>(endP-startP).count();
      cout << "No Chain\t" << count << '\t' << (duration/1.0E9) << " sec \t"
            << (10000.0*size)/(duration) << " GB/s" << endl;
   }
   {
      uint64_t c0 = 0;
      uint64_t c1 = 0;
      uint64_t c2 = 0;
      uint64_t c3 = 0;
      startP = chrono::system_clock::now();
      for( unsigned k = 0; k < 10000; k++){
         for (uint64_t i=0;i<size/8;i+=4) {
            uint64_t r0 = buffer[i + 0];
            uint64_t r1 = buffer[i + 1];
            uint64_t r2 = buffer[i + 2];
            uint64_t r3 = buffer[i + 3];
            __asm__(
                "popcnt %4, %%rax   \n\t"
                "add %%rax, %0      \n\t"
                "popcnt %5, %%rax   \n\t"
                "add %%rax, %1      \n\t"
                "popcnt %6, %%rax   \n\t"
                "add %%rax, %2      \n\t"
                "popcnt %7, %%rax   \n\t"
                "add %%rax, %3      \n\t"
                : "+r" (c0), "+r" (c1), "+r" (c2), "+r" (c3)
                : "r"  (r0), "r"  (r1), "r"  (r2), "r"  (r3)
                : "rax"
            );
         }
      }
      count = c0 + c1 + c2 + c3;
      endP = chrono::system_clock::now();
      duration=chrono::duration_cast<std::chrono::nanoseconds>(endP-startP).count();
      cout << "Chain 4   \t"  << count << '\t' << (duration/1.0E9) << " sec \t"
            << (10000.0*size)/(duration) << " GB/s" << endl;
   }
   {
      uint64_t c0 = 0;
      uint64_t c1 = 0;
      uint64_t c2 = 0;
      uint64_t c3 = 0;
      startP = chrono::system_clock::now();
      for( unsigned k = 0; k < 10000; k++){
         for (uint64_t i=0;i<size/8;i+=4) {
            uint64_t r0 = buffer[i + 0];
            uint64_t r1 = buffer[i + 1];
            uint64_t r2 = buffer[i + 2];
            uint64_t r3 = buffer[i + 3];
            __asm__(
                "xor %%rax, %%rax   \n\t"   // <--- Break the chain.
                "popcnt %4, %%rax   \n\t"
                "add %%rax, %0      \n\t"
                "popcnt %5, %%rax   \n\t"
                "add %%rax, %1      \n\t"
                "popcnt %6, %%rax   \n\t"
                "add %%rax, %2      \n\t"
                "popcnt %7, %%rax   \n\t"
                "add %%rax, %3      \n\t"
                : "+r" (c0), "+r" (c1), "+r" (c2), "+r" (c3)
                : "r"  (r0), "r"  (r1), "r"  (r2), "r"  (r3)
                : "rax"
            );
         }
      }
      count = c0 + c1 + c2 + c3;
      endP = chrono::system_clock::now();
      duration=chrono::duration_cast<std::chrono::nanoseconds>(endP-startP).count();
      cout << "Broken Chain\t"  << count << '\t' << (duration/1.0E9) << " sec \t"
            << (10000.0*size)/(duration) << " GB/s" << endl;
   }

   free(charbuffer);
}

Una referencia igualmente interesante se puede encontrar aquí: http://pastebin.com/kbzgL8si
Este punto de referencia varía el número de popcnt s que están en la cadena de dependencia (falsa).

False Chain 0:  41959360000 0.57748 sec     18.1578 GB/s
False Chain 1:  41959360000 0.585398 sec    17.9122 GB/s
False Chain 2:  41959360000 0.645483 sec    16.2448 GB/s
False Chain 3:  41959360000 0.929718 sec    11.2784 GB/s
False Chain 4:  41959360000 1.23572 sec     8.48557 GB/s




performance actionscript-3 flash optimization