instalar habilitar gratis google desactivar cómo como chrome celular activar javascript c++ performance floating-point benchmarking

habilitar - ¿Por qué parece que JavaScript es 4 veces más rápido que C++?



instalar javascript (3)

Durante mucho tiempo, pensé que C ++ era más rápido que JavaScript. Sin embargo, hoy hice un guión de referencia para comparar la velocidad de los cálculos de punto flotante en los dos idiomas y ¡el resultado es increíble!

¡JavaScript parece ser casi 4 veces más rápido que C ++!

Dejé que ambos idiomas hicieran el mismo trabajo en mi computadora portátil i5-430M, realizando a = a + b por 100000000 veces. C ++ tarda unos 410 ms, mientras que JavaScript solo toma unos 120 ms.

Realmente no tengo idea de por qué JavaScript se ejecuta tan rápido en este caso. ¿Alguien puede explicar eso?

El código que usé para JavaScript es (ejecutado con Node.js):

(function() { var a = 3.1415926, b = 2.718; var i, j, d1, d2; for(j=0; j<10; j++) { d1 = new Date(); for(i=0; i<100000000; i++) { a = a + b; } d2 = new Date(); console.log("Time Cost:" + (d2.getTime() - d1.getTime()) + "ms"); } console.log("a = " + a); })();

Y el código para C ++ (compilado por g ++) es:

#include <stdio.h> #include <ctime> int main() { double a = 3.1415926, b = 2.718; int i, j; clock_t start, end; for(j=0; j<10; j++) { start = clock(); for(i=0; i<100000000; i++) { a = a + b; } end = clock(); printf("Time Cost: %dms/n", (end - start) * 1000 / CLOCKS_PER_SEC); } printf("a = %lf/n", a); return 0; }


Es posible que tenga algunas malas noticias para usted si está en un sistema Linux (que cumple con POSIX al menos en esta situación). La llamada a clock() devuelve el número de tics de reloj consumidos por el programa y escalados por CLOCKS_PER_SEC , que es 1,000,000 .

Eso significa que, si estás en un sistema así, estás hablando en microsegundos para C y milisegundos para JavaScript (según los documentos en línea de JS ). Entonces, en lugar de que JS sea cuatro veces más rápido, C ++ es en realidad 250 veces más rápido.

Ahora puede ser que estés en un sistema en el que CLOCKS_PER_SECOND es algo más que un millón, puedes ejecutar el siguiente programa en tu sistema para ver si está escalado por el mismo valor:

#include <stdio.h> #include <time.h> #include <stdlib.h> #define MILLION * 1000000 static void commaOut (int n, char c) { if (n < 1000) { printf ("%d%c", n, c); return; } commaOut (n / 1000, '',''); printf ("%03d%c", n % 1000, c); } int main (int argc, char *argv[]) { int i; system("date"); clock_t start = clock(); clock_t end = start; while (end - start < 30 MILLION) { for (i = 10 MILLION; i > 0; i--) {}; end = clock(); } system("date"); commaOut (end - start, ''/n''); return 0; }

La salida en mi caja es:

Tuesday 17 November 11:53:01 AWST 2015 Tuesday 17 November 11:53:31 AWST 2015 30,001,946

Demostrando que el factor de escala es un millón. Si ejecuta ese programa, o investiga CLOCKS_PER_SEC y no es un factor de escala de un millón, debe mirar otras cosas.

El primer paso es asegurarse de que el compilador está optimizando su código. Eso significa, por ejemplo, la configuración de -O2 o -O2 para gcc .

En mi sistema con código no optimizado, veo:

Time Cost: 320ms Time Cost: 300ms Time Cost: 300ms Time Cost: 300ms Time Cost: 300ms Time Cost: 300ms Time Cost: 300ms Time Cost: 300ms Time Cost: 300ms Time Cost: 300ms a = 2717999973.760710

y es tres veces más rápido con -O2 , aunque con una respuesta ligeramente diferente, aunque solo en una millonésima parte del porcentaje:

Time Cost: 140ms Time Cost: 110ms Time Cost: 100ms Time Cost: 100ms Time Cost: 100ms Time Cost: 100ms Time Cost: 100ms Time Cost: 100ms Time Cost: 100ms Time Cost: 100ms a = 2718000003.159864

Eso haría que las dos situaciones volvieran a la par una con la otra, algo que esperaría ya que JavaScript no es una bestia interpretada como en los viejos tiempos, donde cada token se interpreta cada vez que se ve.

Los motores de JavaScript modernos (V8, Rhino, etc.) pueden compilar el código en una forma intermedia (o incluso en lenguaje de máquina) lo que puede permitir un rendimiento más o menos igual a los lenguajes compilados como C.

Pero, para ser honesto, no tiendes a elegir JavaScript o C ++ por su velocidad, los eliges por sus áreas de fortaleza. No hay muchos compiladores de C flotando dentro de los navegadores y no he notado muchos sistemas operativos ni aplicaciones incrustadas escritas en JavaScript.


Este es un tema polarizador, por lo que uno puede echar un vistazo a:

https://benchmarksgame-team.pages.debian.net/benchmarksgame/

Benchmarking de todo tipo de idiomas.

Javascript V8 y otros hacen un buen trabajo para bucles simples como en el ejemplo, probablemente generando un código de máquina muy similar. Para la mayoría de las aplicaciones "cercanas al usuario", seguramente Javscript es la mejor opción, pero tenga en cuenta el desperdicio de memoria y el éxito (y falta de control) muchas veces inevitable para algoritmos / aplicaciones más complicados.


Haciendo una prueba rápida al activar la optimización, obtuve resultados de aproximadamente 150 ms para un antiguo procesador AMD 64 X2 y aproximadamente 90 ms para un procesador Intel i7 razonablemente reciente.

Luego hice un poco más para dar una idea de una razón por la que podrías querer usar C ++. Desenrollé cuatro iteraciones del bucle, para obtener esto:

#include <stdio.h> #include <ctime> int main() { double a = 3.1415926, b = 2.718; double c = 0.0, d=0.0, e=0.0; int i, j; clock_t start, end; for(j=0; j<10; j++) { start = clock(); for(i=0; i<100000000; i+=4) { a += b; c += b; d += b; e += b; } a += c + d + e; end = clock(); printf("Time Cost: %fms/n", (1000.0 * (end - start))/CLOCKS_PER_SEC); } printf("a = %lf/n", a); return 0; }

Esto permitió que el código C ++ se ejecutara en aproximadamente 44 ms en la AMD (se olvidó de ejecutar esta versión en Intel). Luego encendí el auto-vectorizador del compilador (-Qpar con VC ++). Esto redujo aún más el tiempo, a aproximadamente 40 ms en el AMD, y 30 ms en la Intel.

En pocas palabras: si desea usar C ++, realmente necesita aprender a usar el compilador. Si desea obtener resultados realmente buenos, probablemente también desee aprender a escribir un código mejor.

Debería agregar: No intenté probar una versión en Javascript con el bucle desenrollado. Hacerlo también podría proporcionar una mejora de velocidad similar (o al menos algo) en JS. Personalmente, creo que hacer que el código sea rápido es mucho más interesante que comparar Javascript con C ++.

Si desea que un código como este se ejecute rápidamente, desenrolle el bucle (al menos en C ++).

Dado que surgió el tema de la computación paralela, pensé que agregaría otra versión utilizando OpenMP. Mientras estaba en eso, limpié un poco el código para poder hacer un seguimiento de lo que estaba pasando. También cambié un poco el código de tiempo para mostrar el tiempo total en lugar del tiempo para cada ejecución del bucle interno. El código resultante se veía así:

#include <stdio.h> #include <ctime> int main() { double total = 0.0; double inc = 2.718; int i, j; clock_t start, end; start = clock(); #pragma omp parallel for reduction(+:total) firstprivate(inc) for(j=0; j<10; j++) { double a=0.0, b=0.0, c=0.0, d=0.0; for(i=0; i<100000000; i+=4) { a += inc; b += inc; c += inc; d += inc; } total += a + b + c + d; } end = clock(); printf("Time Cost: %fms/n", (1000.0 * (end - start))/CLOCKS_PER_SEC); printf("a = %lf/n", total); return 0; }

La adición principal aquí es la siguiente línea (ciertamente algo arcana):

#pragma omp parallel for reduction(+:total) firstprivate(inc)

Esto le indica al compilador que ejecute el bucle externo en varios subprocesos, con una copia separada de inc para cada subproceso, y sumando los valores individuales del total después de la sección paralela.

El resultado es sobre lo que probablemente esperaría. Si no habilitamos OpenMP con el indicador del compilador -openmp , el tiempo informado es aproximadamente 10 veces lo que vimos anteriormente para ejecuciones individuales (409 ms para el AMD, 323 MS para el Intel). Con OpenMP activado, los tiempos se reducen a 217 ms para el AMD y 100 ms para el Intel.

Por lo tanto, en Intel, la versión original tomó 90 ms para una iteración del bucle externo. Con esta versión, nos estamos volviendo un poco más largos (100 ms) para las 10 iteraciones del bucle externo, una mejora en la velocidad de aproximadamente 9: 1. En una máquina con más núcleos, podríamos esperar aún más mejoras (normalmente, OpenMP aprovechará todos los núcleos disponibles automáticamente, aunque puede ajustar manualmente la cantidad de subprocesos si lo desea).