tipos practico objetos objects examen ejemplos datos clonar buscar array agregar javascript c arrays memory typed-arrays

practico - map javascript



¿Las ventajas de los arrays tipados en JavaScript son que funcionan de la misma manera o similares en C? (4)

Cuando se trata de rendimiento, las cosas pueden cambiar rápidamente. Como dice AshleysBrain, todo depende de si la máquina virtual puede deducir que una matriz normal puede implementarse como una matriz mecanografiada de manera rápida y precisa. Eso depende de las optimizaciones particulares de la máquina virtual JavaScript en particular, y puede cambiar en cualquier nueva versión del navegador.

Este comentario del desarrollador de Chrome proporciona una guía que funcionó a partir de junio de 2012:

  1. Las matrices normales pueden ser tan rápidas como las matrices escritas si realiza una gran cantidad de acceso secuencial. El acceso aleatorio fuera de los límites de la matriz hace que la matriz crezca.
  2. Los arreglos escritos son rápidos para el acceso, pero son lentos para ser asignados. Si crea matrices temporales con frecuencia, evite las matrices escritas. (Arreglar esto es posible, pero es de baja prioridad).
  3. Los micro-puntos de referencia como JSPerf no son confiables para el desempeño en el mundo real.

Si pudiera explicar el último punto, he visto este fenómeno con Java durante años. Cuando prueba la velocidad de un pequeño fragmento de código ejecutándolo una y otra vez de forma aislada, la máquina virtual optimiza el diablo de ella. Hace optimizaciones que solo tienen sentido para esa prueba específica. Su punto de referencia puede obtener una mejora de la velocidad cien veces mayor en comparación con la ejecución del mismo código dentro de otro programa, o en comparación con la ejecución inmediatamente después de ejecutar varias pruebas diferentes que optimizan el mismo código de manera diferente.

He estado jugando con Typed Arrays en JavaScript.

var buffer = new ArrayBuffer(16); var int32View = new Int32Array(buffer);

Me imagino que los arreglos normales ( [1, 257, true]) en JavaScript tienen un bajo rendimiento porque sus valores podrían ser de cualquier tipo, por lo tanto, alcanzar un desplazamiento en la memoria no es trivial.

Originalmente pensé que los subíndices de la matriz de JavaScript funcionaban igual que los objetos (ya que tienen muchas similitudes), y se basaban en el mapa hash , lo que requería una búsqueda basada en hash. Pero no he encontrado mucha información creíble para confirmar esto.

Por lo tanto, supongo que la razón por la que los arrays tipificados funcionan tan bien es porque funcionan como arrays normales en C, donde siempre se escriben. Dado el ejemplo de código inicial anterior, y deseando obtener el décimo valor en la matriz escrita ...

var value = int32View[10];

  • El tipo es Int32 , por lo que cada valor debe constar de 32 bits o 4 bytes.
  • El subíndice es 10 .
  • Así que la ubicación en la memoria de ese valor es <array offset> + (4 * 10) , y luego lee 4 bytes para obtener el valor total.

Básicamente solo quiero confirmar mis suposiciones. ¿Mis pensamientos acerca de esto son correctos? Y si no, por favor explique.

Revisé la fuente de V8 para ver si podía responderla yo mismo, pero mi C está oxidada y no estoy muy familiarizado con C ++.


Los arreglos escritos fueron diseñados por el comité de estándares de WebGL, por razones de rendimiento. Por lo general, las matrices de Javascript son genéricas y pueden contener objetos, otras matrices, etc., y los elementos no son necesariamente secuenciales en la memoria, como lo estarían en C. WebGL requiere que los buffers sean secuenciales en la memoria, porque así es como espera la API de C subyacente ellos. Si no se utilizan matrices tipográficas, pasar una matriz ordinaria a una función de WebGL requiere mucho trabajo: se debe inspeccionar cada elemento, se debe verificar el tipo y, si es lo correcto (por ejemplo, una flotación), se debe copiar en una secuencia separada. C-como búfer, luego pasar ese búfer secuencial a la API de C. ¡Ay, mucho trabajo! Para aplicaciones WebGL sensibles al rendimiento, esto podría causar una gran caída en el framerate.

Por otro lado, como sugiere en la pregunta, los arreglos de tipos utilizan un búfer tipo C secuencial que ya se encuentra en su almacenamiento tras bambalinas. Cuando escribes en una matriz escrita, de hecho estás asignando una matriz tipo C detrás de escena. Para los propósitos de WebGL, esto significa que el búfer puede ser usado directamente por la API C correspondiente.

Tenga en cuenta que el cálculo de la dirección de la memoria no es suficiente: el navegador también debe controlar los límites de la matriz para evitar accesos fuera de rango. Esto tiene que suceder con cualquier tipo de matriz de Javascript, pero en muchos casos los motores inteligentes de Javascript pueden omitir la comprobación cuando puede probar que el valor del índice ya está dentro de los límites (como hacer un bucle de 0 a la longitud de la matriz). ¡También tiene que comprobar que el índice de matriz es realmente un número y no una cadena o algo más! Pero es, en esencia, como usted describe, utilizando direccionamiento tipo C.

PERO ... eso no es todo! En algunos casos, los motores de Javascript inteligentes también pueden deducir el tipo de matrices de JavaScript ordinarias . En un motor como V8, si crea una matriz de Javascript ordinaria y solo almacena flotadores en ella, V8 puede decidir con optimismo que es una matriz de flotadores y optimizar el código que genera para eso. El rendimiento puede ser equivalente a las matrices escritas. Por lo tanto, los arreglos escritos no son realmente necesarios para alcanzar el máximo rendimiento: solo se pueden usar arreglos predeciblemente (con todos los elementos del mismo tipo) y algunos motores también pueden optimizar para eso.

Entonces, ¿por qué las matrices mecanografiadas todavía necesitan existir?

  • Optimizaciones como deducir el tipo de arreglos es realmente complicado . Si V8 deduce que una matriz ordinaria solo tiene flotadores en ella, entonces usted almacena un objeto en un elemento, debe des-optimizar y regenerar el código que hace que la matriz vuelva a ser genérica. Es todo un logro que todo esto funcione de manera transparente. Los arrays tipados son mucho más simples: están garantizados para ser de un solo tipo, y simplemente no puedes almacenar otras cosas como objetos en ellos.
  • Nunca se garantiza que las optimizaciones ocurran; puede almacenar solo flotadores en una matriz ordinaria, pero el motor puede decidir, por varias razones, no optimizarlo.
  • El hecho de que sean mucho más simples significa que otros motores javascript menos sofisticados pueden implementarlos fácilmente. No necesitan todo el soporte de desoptimización avanzado.
  • Incluso con motores realmente avanzados, demostrar que las optimizaciones pueden usarse es extremadamente difícil y algunas veces puede ser imposible. Una matriz mecanografiada simplifica significativamente el nivel de prueba que el motor necesita para poder optimizar a su alrededor. Un valor devuelto desde una matriz escrita es ciertamente de un cierto tipo, y los motores pueden optimizar para que el resultado sea ese tipo. En teoría, un valor devuelto desde una matriz ordinaria podría tener cualquier tipo, y el motor puede no ser capaz de demostrar que siempre tendrá el mismo resultado y, por lo tanto, genera un código menos eficiente. Por lo tanto, el código alrededor de una matriz escrita se optimiza más fácilmente.
  • Las matrices mecanografiadas eliminan la oportunidad de cometer un error. Simplemente no puede almacenar un objeto accidentalmente y de repente obtener un rendimiento mucho peor.

Así que, en resumen, los arreglos ordinarios pueden en teoría ser tan rápidos como los arreglos escritos. Pero los arreglos escritos hacen que sea mucho más fácil alcanzar el máximo rendimiento.


Realmente no contribuyo a ningún motor de JavaScript, solo tuve algunas lecturas en v8, por lo que mi respuesta podría no ser completamente cierta:

Los valores de pozo en los arrays (solo los arrays normales sin huecos / huecos, no dispersos. Los arrays dispersos se tratan como objetos.) Son todos punteros o un número con una longitud fija (en v8 son de 32 bits, si es un entero de 31 bits está etiquetado con un bit 0 al final, de lo contrario es un puntero).

Por lo tanto, no creo que encontrar la ubicación de la memoria sea diferente a typedArray, ya que el número de bytes es el mismo en toda la matriz. Pero la diferencia es que si se trata de un objeto, entonces tiene que agregar una capa unboxing, lo que no ocurre con los arrays de tipo normal.

Y, por supuesto, cuando acceda a TypedArrays, definitivamente no tiene la comprobación de tipos que tiene una matriz normal (aunque eso podría eliminarse en un código altamente optimizado, que solo se genera para el código activo).

Para escribir, si es del mismo tipo no debería ser mucho más lento. Si es un tipo diferente, entonces el motor JS podría generar un código polimórfico para él, que es más lento.

También puede intentar hacer algunos puntos de referencia en jsperf.com para confirmar.


Sí, usted es mayormente correcto. Con una matriz de JavaScript estándar, el motor de JavaScript debe asumir que los datos de la matriz son todos los objetos. Todavía puede almacenar esto como una matriz / vector tipo C, donde el acceso a la memoria sigue siendo como usted describió. El problema es que los datos no son el valor, sino algo que hace referencia a ese valor (el objeto).

Por lo tanto, realizar a[i] = b[i] + 2 requiere que el motor:

  1. acceder al objeto en b en el índice i;
  2. compruebe qué tipo de objeto es;
  3. extraer el valor del objeto;
  4. agregue 2 al valor;
  5. crear un nuevo objeto con el valor recién calculado a partir de 4;
  6. asigne el nuevo objeto del paso 5 a un índice i.

Con una matriz escrita, el motor puede:

  1. acceder al valor en b en el índice i (incluyendo colocarlo en un registro de CPU);
  2. incrementar el valor en 2;
  3. asigne el nuevo objeto del paso 2 a un índice i.

NOTA: Estos no son los pasos exactos que llevará a cabo un motor de JavaScript, ya que eso depende del código compilado (incluido el código circundante) y del motor en cuestión.

Esto permite que los cálculos resultantes sean mucho más eficientes. Además, los arreglos escritos tienen una garantía de diseño de memoria (arreglos de valores de n bytes) y, por lo tanto, se pueden utilizar para interactuar directamente con los datos (audio, video, etc.).