Скорость выполнения кода JavaScript
Написал некий интерпретатор арифметических выражений на C++ и затем перенес эту реализацию на JavaScript (с сохранением структуры алгоритмов на сколько это было возможно, учитывая различия в грамматике языков).
Меня интересует скорость вычисления интерпретированного выражения. Для оценки скорости я использовал отношение времени вычисления моим калькулятором ко времени вычисления тождественного выражения машиной.
В случае C++ это отношение было близко к 2, а в случае JavaScript оно возрастает до 20!
Хотелось бы выслушать рекомендации общего (или частного) характера по увеличению скорости исполнения кода JavaScript.
Немного конкретики:
Код:
//Парсер преобразует строку во внутреннее представление
//и формирует объект следующего вида
var expression = {
expr : [
{src:"x", type:VAR, pr:0, idx:0, val:null},
{src:"*", type:ALB, pr:30, idx:4, val:null},
{src:"y", type:VAR, pr:0, idx:1, val:null}
]
};
//------------------------------------------
//Работа калькулятора
//------------------------------------------
function Calculator (e) {
this.expr = this.MakeRPN(e);//Перевод в обратную польскую нотацию
this.size = this.expr.length;
this.vars = [];
this.i = 0;
this.r = 0.0;
this.l = 0.0;
this.k = 0;
}
//Вычисляющая функция
//CALCSTACK - просто глобальный массив
//CALLSTACK - массив вычисляемых функций
Calculator.prototype.Calc = function (variables) {
this.vars = variables;
for(this.k=0;this.k<this.size;++this.k)
switch(this.expr[this.k].type){
case NUM :
case CON : CALCSTACK[this.i++] = this.expr[this.k].val;
break;//константные значения
case VAR : CALCSTACK[this.i++] = this.vars[this.expr[this.k].idx];
break;//переменные
case ALB : this.r = CALCSTACK[--this.i];//бинарные операции
case ALU : this.l = CALCSTACK[--this.i];//унарные операции
CALCSTACK[this.i++] = CALLSTACK[this.expr[this.k].idx](this.l,this.r);
break;
};
return CALCSTACK[--this.i];
}
//------------------------------------------
//Тест
//------------------------------------------
var cl = new Calculator (expression),
re, start, end;
var xy = [5.0,2.0];
start = new Date();
for(var i=1;i<=100000000;i++){
xy[0] = i;
re = cl.Calc(xy);
}
end = new Date();
alert('Скорость ' + (end.getTime()-start.getTime()) + ' мс');
//примерно 4300 мс
start = new Date();
for(var i=1;i<=100000000;i++){
xy[0] = i;
re = xy[0]*xy[1];
}
end = new Date();
alert('Скорость ' + (end.getTime()-start.getTime()) + ' мс');
//примерно 230 мс
//и формирует объект следующего вида
var expression = {
expr : [
{src:"x", type:VAR, pr:0, idx:0, val:null},
{src:"*", type:ALB, pr:30, idx:4, val:null},
{src:"y", type:VAR, pr:0, idx:1, val:null}
]
};
//------------------------------------------
//Работа калькулятора
//------------------------------------------
function Calculator (e) {
this.expr = this.MakeRPN(e);//Перевод в обратную польскую нотацию
this.size = this.expr.length;
this.vars = [];
this.i = 0;
this.r = 0.0;
this.l = 0.0;
this.k = 0;
}
//Вычисляющая функция
//CALCSTACK - просто глобальный массив
//CALLSTACK - массив вычисляемых функций
Calculator.prototype.Calc = function (variables) {
this.vars = variables;
for(this.k=0;this.k<this.size;++this.k)
switch(this.expr[this.k].type){
case NUM :
case CON : CALCSTACK[this.i++] = this.expr[this.k].val;
break;//константные значения
case VAR : CALCSTACK[this.i++] = this.vars[this.expr[this.k].idx];
break;//переменные
case ALB : this.r = CALCSTACK[--this.i];//бинарные операции
case ALU : this.l = CALCSTACK[--this.i];//унарные операции
CALCSTACK[this.i++] = CALLSTACK[this.expr[this.k].idx](this.l,this.r);
break;
};
return CALCSTACK[--this.i];
}
//------------------------------------------
//Тест
//------------------------------------------
var cl = new Calculator (expression),
re, start, end;
var xy = [5.0,2.0];
start = new Date();
for(var i=1;i<=100000000;i++){
xy[0] = i;
re = cl.Calc(xy);
}
end = new Date();
alert('Скорость ' + (end.getTime()-start.getTime()) + ' мс');
//примерно 4300 мс
start = new Date();
for(var i=1;i<=100000000;i++){
xy[0] = i;
re = xy[0]*xy[1];
}
end = new Date();
alert('Скорость ' + (end.getTime()-start.getTime()) + ' мс');
//примерно 230 мс
Код:
re = cl.Calc(xy);
Код:
re = cl.Calc(xy);// это объект
Код:
re = xy[0]*xy[1];// это переменные
Вообще, из замечания:
можно сделать вывод, что для ускорения работы программы надо стараться везде работать с переменными, а не с объектами, однако, если буквально понимать утверждение:
, то это представляется потенциально проблематичным.
Некоторые приложения (и графические, и вычислительные) на JS, работают достаточно быстро, по крайней мере создают такое впечатление. То-есть, интерпретаторы JS претерпели существенную оптимизацию производительности по сравнению с первыми версиями.
Просто стало неожиданностью, что интерпретатор написанный на JS работает в 20 раз медленнее чем сам JS. В то время как аналогичный интерпретатор написанный на C++ и скомпилированный замедляется, всего, в два раза.