Porque é que o JavaScript parece ser 4 vezes mais rápido que o C++?
o JavaScript parece ser quase 4 vezes mais rápido que o C++!
Deixei que ambas as línguas fizessem o mesmo trabalho no meu laptop i5-430M, realizando 100 milhões de vezes. O C++ leva cerca de 410 ms, enquanto o JavaScript leva apenas cerca de 120 ms. A sério. não faço ideia porque o JavaScript corre tão rápido neste caso. Alguém pode explicar isso?o código que usei para o JavaScript é (execute com nó.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);
})();
e o código para C++ (compilado por g++) é:
#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;
}
3 answers
Posso ter más notícias para si se estiver num sistema Linux (que está em conformidade com o POSIX pelo menos nesta situação). O clock()
a chamada devolve o número de carraças de relógio consumidas pelo programa e escaladas por CLOCKS_PER_SEC
, que é 1,000,000
.
Que significa, se você estiver em tal sistema, você está falando em microssegundos para C e milissegundos para JavaScript (como por a JS documentos on-line). Então, em vez de JS ser quatro vezes mais rápido, C++ é 250 vezes mais rápido.
Agora pode ser que você esteja em um sistema ondeCLOCKS_PER_SECOND
é algo diferente de um milhão, você pode executar o seguinte programa em seu sistema para ver se ele é dimensionado pelo mesmo 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;
}
A saída na minha caixa é:
Tuesday 17 November 11:53:01 AWST 2015
Tuesday 17 November 11:53:31 AWST 2015
30,001,946
A mostrar que o factor de escala é um milhão. Se você executar esse programa, ou investigar CLOCKS_PER_SEC
e não um fator de escala de um milhão, você precisa olhar para algum outro situacao.
O primeiro passo é garantir que o seu código está a ser optimizado pelo compilador. Isso significa, por exemplo, colocar -O2
ou -O3
para gcc
.
No meu sistema com um código não optimizado, vejo:
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
E é três vezes mais rápido com -O2
, embora com uma resposta ligeiramente diferente, embora apenas por cerca de um milionésimo de por cento:
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
Isso traria as duas situações de volta ao mesmo nível uma da outra, algo que eu esperaria desde então. JavaScript não é uma besta interpretada como nos velhos tempos, onde cada símbolo é interpretado sempre que é visto.
Motores JavaScript modernos (V8, Rhino, etc) podem compilar o código para uma forma intermediária (ou mesmo para linguagem de máquina) que pode permitir um desempenho aproximadamente igual a linguagens compiladas como C.
Mas, para ser honesto, você não tende a escolher JavaScript ou C++ para sua velocidade, você os escolhe para suas Áreas de força. Não há muitos compiladores C flutuando em torno de navegadores internos e eu não notei muitos sistemas operacionais nem aplicativos incorporados escritos em JavaScript.
Então eu fiz um pouco mais para dar uma ideia de uma razão para você querer usar C++. Eu desenrolei quatro iterações do loop, para conseguir isto:
#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;
}
Isto permite que o código C++ corra em cerca de 44ms no AMD (esqueceu-se de executar esta versão no Intel). Depois liguei o auto-vectorizador do compilador. (- Qpar com VC++). Isso reduziu o tempo um pouco mais ainda, para cerca de 40 ms no AMD, e 30 ms no Intel.
Conclusão: se quiser usar o C++, tem de aprender a usar o compilador. Se você quer obter realmente bons resultados, você provavelmente também quer aprender a escrever um código melhor.
Devo acrescentar que não tentei testar uma versão em Javascript com o loop desenrolado. Fazê-lo pode proporcionar uma melhoria similar (ou pelo menos alguma) da velocidade em JS também. Pessoalmente, acho que fazer o código rápido é muito mais interessante do que comparar Javascript com C++.Se quiser que um código como este corra rápido, desenrole o loop (pelo menos em C++).
Uma vez que o assunto da computação paralela surgiu, pensei em Adicionar outra versão usando OpenMP. Enquanto estava lá, limpei um pouco o código, para poder acompanhar o que se estava a passar. Eu também alterei o código de tempo um pouco, para exibir o tempo total em vez do tempo para cada execução do laço interior. O código resultante era assim:#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;
}
A adição primária aqui é a seguinte linha (reconhecidamente algo Arcana):
#pragma omp parallel for reduction(+:total) firstprivate(inc)
Isto diz ao compilador para executar o laço exterior em vários tópicos, com uma cópia separada de inc
para cada tópico, e adicionando os valores individuais de total
Após a secção paralela.
-openmp
, O Relatório o tempo é cerca de 10 vezes o que vimos anteriormente para execuções individuais (409 ms para a AMD, 323 MS para a Intel). Com o OpenMP ligado, o tempo passa para 217 ms para o AMD, e 100 ms para as informações.
Então, segundo as informações, a versão original levou 90m por uma iteração do laço exterior. Com esta versão estamos ficando apenas um pouco mais longo (100 ms) para todas as 10 iterações do laço exterior -- uma melhoria na velocidade de cerca de 9:1. Numa máquina com mais núcleos, poderíamos esperar ainda mais melhoria (o OpenMP normalmente tirará vantagem de todos os núcleos disponíveis automaticamente, embora você possa afinar manualmente o número de threads se quiser).
Https://benchmarksgame-team.pages.debian.net/benchmarksgame/
([1]) aferir todos os tipos de línguas.Javascript V8 e tal estão certamente fazendo um bom trabalho para loops simples como no exemplo, provavelmente gerando código de máquina muito semelhante. Para a maioria das aplicações" perto do Usuário " Javscript certamente é a melhor escolha, mas tenha em mente o desperdício de memória e as muitas vezes inevitáveis performance hit (and lack of control) for more complicated algorithms/applications.