Как это ускорить (почему это так медленно работает)?
Немного о задаче:
У меня есть функция (js), которая принимает аргумент и возвращает вычисленное по математической формуле значение
var f = function(x) {
return 3*Math.sin(2*x)*Math.pow(Math.cos(x), 4);
}
Также у меня есть массив, индекс - номер интервала (индексация с нуля), значение элемента по индексу - кол-во раз, когда значение попадало в этот индекс.
Например, если у меня в массиве 2 элемента, и значения которые меня интересуют от 0 до 1, то если f(x) вернуло число, меньшее 0,5, то первый элемент массива увеличиться на 1, если большее 0,5, то второй элемент соответственно увеличится на 1.
Я модифицировал функцию, чтобы она сразу возвращала, тот номер интервала, в который попадёт значение при данном аргументе.
var hist_min = 0;
var hist_max = 2;
var hist_range = hist_max-hist_min;
var hist_cols = 30;
var f = function(x) {
return Math.floor(((3*Math.sin(2*x)*Math.pow(Math.cos(x), 4)-hist_min)/hist_range) * hist_cols);
}
В приведённом выше примере у меня 30 интервалов, левая граница самого левого - 0, правая правого - 2.
Далее я замеряю скорость выполнения 3*10^6 итераций этой функции.
var iteratitions = 3000000;
var start_time = Date.now();
for (var i=0; i < iteratitions; i++) {
var need_col = f(Math.random())
}
console.log("Finished in",Date.now()-start_time+"ms")
получается довольно быстро, на моём компьютере ~100-200ms
Однако, мне нужно как-то сохранять эти интервалы, например в массив.
Я делаю так
var start_time = Date.now();
for (var i=0; i < iteratitions; i++) {
var need_col = f(Math.random())
arr[need_col]++;
}
console.log("Finished in",Date.now()-start_time+"ms")
Однако так уже выполняется значительно дольше ~1300ms
Сначала я думал, это из-за того, что arr[need_col]++ сильно долго выполняется, однако если сделать так:
for (var i=0; i < iteratitions; i++) {
var need_col = f(Math.random())
var need_col2 = 3
arr[need_col2]++;
}
И это выполняется уже за ~130ms
Возникает вопрос, почему если я просто сохраняю значение функции f в переменную need_col, всё работает быстро, а как только я использую переменную need_col (передаю как индекс), то всё замедляется в разы?
Привожу весь код
var hist_min = 0;
var hist_max = 2;
var hist_range = hist_max-hist_min;
var hist_cols = 30;
var need_col = 0;
/*
var f = function(x) {
return 3*Math.sin(2*x)*Math.pow(Math.cos(x), 4)
}
*/
var f = function(x) {
return Math.floor(((3*Math.sin(2*x)*Math.pow(Math.cos(x), 4)-hist_min)/hist_range) * hist_cols);
}
var iteratitions = 3000000;
var arr = [];
// инициализация
for (var i=0; i < hist_cols; i++) {
arr.push(0)
}
// пустой цикл занимает время
var start_time = Date.now();
for (var i=0; i < iteratitions; i++) {
}
console.log("empty loop finished in",Date.now()-start_time+"ms")
// просто увеличение элемента массива по индексу
var start_time = Date.now();
for (var i=0; i < iteratitions; i++) {
var need_col = 3
arr[need_col]++;
}
console.log("arr[need_col]++; Finished in",Date.now()-start_time+"ms")
// просто выполение f(x)
var start_time = Date.now();
for (var i=0; i < iteratitions; i++) {
need_col = f(Math.random())
//var need_col2 = 3
//arr[need_col2]++;
}
console.log("just f(x) finished in",Date.now()-start_time+"ms")
//console.log(need_col)
// заполнение массива тем, что нам нужно
var start_time = Date.now();
for (var i=0; i < iteratitions; i++) {
var need_col = f(Math.random())
arr[need_col]++;
}
console.log("filling array f(x) finished in",Date.now()-start_time+"ms")
console.log("arr=", arr)
P.S. на моём компьютере немного другие цифры (я выполняю код через node)

Ответы (2 шт):
Индекс это строка, разница во времени посути в need_col.toString() внутри гетера массива.
new Proxy ([ 1 ],{
get : function ( target , key ){
console . log ( target , typeof key , key );
// [ 1 ] string 0
}
})[ 0 ] ++ ;
В данном конкретном вопросе проблема в том, что функция 3*Math.sin(2*x)*Math.pow(Math.cos(x), 4) на отрезке [0, 1) имеет значения о 0 до примерно 1.553, но функция пересчёта в номер интервала (до изменения вопроса) была рассчитана на значения от 0 до 1. Поэтому для значений больше 1 вызов arr[need_col]++ выходил за границы массива и это приводило к сильному замедлению (и значениям NaN в массиве).
Сравнение for(..) { f(x); } и for(..) { arr[f(x)]++; } некорректно. В первом случае движок понимает, что результат вызова функции не используется и экономит на возврате значения. А если заметит, что это вообще чистая функция, то в теории может её вообще не вызывать.
Корректнее сравнивать с (например) s = 0; for(..) { s += f(x) }, и тут результат не сильно отличается от доступа к массиву.
Таким образом можно считать, что всё что можно было сделать в рамках JS уже сделано. Если нужно быстрее, то надо смотреть в сторону других ЯП, но это выходит за рамки данного вопроса.