简体   繁体   English

为数组赋值的性能

[英]Performance of assigning values to array

Code optimizing is said here in SO that profiling is the first step for optimizing javascript and the suggested engines are profilers of Chrome and Firefox. 代码优化在这里说,分析是优化javascript的第一步,建议的引擎是Chrome和Firefox的分析器。 The problem with those is that they tell in some weird way the time that each function is executed, but I haven't got any good understanding of them. 这些问题是他们以某种奇怪的方式告诉每个函数执行的时间,但我对它们没有很好的理解。 The most helpful way would be that the profiler would tell, how many times each row is executed and if ever possible also the time that is spent on each row. 最有用的方法是分析器会告诉每行执行的次数,以及每行可能花费的时间。 This way would it be possible to see the bottlenecks strictly. 这样就可以严格地看到瓶颈。 But before such tool is implemented/found, we have two options: 但在实施/找到此类工具之前,我们有两种选择:

1) make own calculator which counts both the time and how many times certain code block or row is executed 2) learn to understand which are slow methods and which are not 1)制作自己的计算器,计算某个代码块或行执行的时间和次数2)学会理解哪些是慢速方法,哪些不是

For option 2 jsperf.com is of great help. 对于选项2, jsperf.com非常有帮助。 I have tried to learn optimizing arrays and made a speed test in JSPERF.COM . 我试图学习优化数组并在JSPERF.COM中进行速度测试。 The following image shows the results in 5 main browsers and found some bottlenecks that I didn't know earlier. 下图显示了5个主要浏览器的结果,发现了一些我之前不知道的瓶颈。

速度测试

The main findings were: 主要调查结果如下:

1) Assigning values to arrays is significantly slower than assigning to normal variables despite of which method is used for assigning. 1)尽管使用哪种方法进行分配,但为数组分配值要比分配给正常变量要慢得多。

2) Preinitializing and/or prefilling array before performance critical loops can improve speed significantly 2)在性能关键循环之前预初始化和/或预填充阵列可以显着提高速度

3) Math trigonometric functions are not so slow when compared to pushing values into arrays(!) 3)与将数值推入数组相比,数学三角函数不是那么慢(!)

Here are the explanations of every test: 以下是每项测试的解释:


1. non_array (100%): 1. non_array(100%):

The variables were given a predefined value this way: 变量以这种方式给出了预定义的值:

var non_array_0=0;
var non_array_1=0;
var non_array_2=0;
...

and in timed region they were called this way: 在定时区域,他们被称为这样:

non_array_0=0;
non_array_1=1;
non_array_2=2;
non_array_3=3;
non_array_4=4;
non_array_5=5;
non_array_6=6;
non_array_7=7;
non_array_8=8;
non_array_9=9;

The above is an array-like variable, but there seems to be no way to iterate or refer to those variables in other way as oppocite to array. 上面是一个类似数组的变量,但似乎无法以其他方式迭代或引用这些变量作为对阵列的反对。 Or is there? 还是有吗?

Nothing in this test is faster than assigning a number to variable. 此测试中的任何内容都不比为变量赋值更快。


2. non_array_non_pre (83.78%) 2. non_array_non_pre(83.78%)

Exactly the same as test 1, but the variables were not pre-initialized nor prefilled. 与测试1完全相同,但变量未预先初始化或预先填充。 The speed is 83,78% of the speed of test 1. In every tested browser the speed of prefilled variables was faster than non-prefilled. 速度是测试速度的83.78%。在每个测试的浏览器中,预填充变量的速度比未预先填充的速度快。 So initialize (and possibly prefill) variables outside any speed critical loops. 因此,在任何速度关键循环之外初始化(并可能预填充)变量。

The test code is here: 测试代码在这里:

var non_array_non_pre_0=0;
var non_array_non_pre_1=0;
var non_array_non_pre_2=0;
var non_array_non_pre_3=0;
var non_array_non_pre_4=0;
var non_array_non_pre_5=0;
var non_array_non_pre_6=0;
var non_array_non_pre_7=0;
var non_array_non_pre_8=0;
var non_array_non_pre_9=0;

3. pre_filled_array (19.96 %): 3. pre_filled_array(19.96%):

Arrays are evil! 阵列是邪恶的! When we throw away normal variables (test1 and test2) and take arrays in to the picture, the speed decreases significantly. 当我们丢弃正常变量(test1和test2)并将数组放入图片时,速度会显着降低。 Although we make all optimizations (preinitialize and prefill arrays) and then assign values directly without looping or pushing, the speed decreases to 19.96 percent. 虽然我们进行了所有优化(预初始化和预填充数组),然后直接分配值而不进行循环或推送,但速度降低到19.96%。 This is very sad and I really don't understand why this occurs. 这非常难过,我真的不明白为什么会这样。 This was one of the main shocks to me in this test. 这是我在这次测试中受到的主要冲击之一。 Arrays are so important, and I have not find a way to make many things without arrays. 数组是如此重要,我没有找到一种方法来制作没有数组的很多东西。

The test data is here: 测试数据在这里:

pre_filled_array[0]=0;
pre_filled_array[1]=1;
pre_filled_array[2]=2;
pre_filled_array[3]=3;
pre_filled_array[4]=4;
pre_filled_array[5]=5;
pre_filled_array[6]=6;
pre_filled_array[7]=7;
pre_filled_array[8]=8;
pre_filled_array[9]=9;

4. non_pre_filled_array (8.34%): 4. non_pre_filled_array(8.34%):

This is the same test as 3, but the array members are not preinitialized nor prefilled, only optimization was to initialize the array beforehand: var non_pre_filled_array=[]; 这与3的测试相同,但是数组成员不是预先初始化的,也不是预先var non_pre_filled_array=[]; ,只有优化是事先初始化数组: var non_pre_filled_array=[];

The speed decreases 58,23 % compared to preinitilized test 3. So preinitializing and/or prefilling array over doubles the speed. 与预先确定的测试3相比,速度降低了58,23%。 因此预先初始化和/或预填充阵列使速度加倍。

The test code is here: 测试代码在这里:

non_pre_filled_array[0]=0;
non_pre_filled_array[1]=1;
non_pre_filled_array[2]=2;
non_pre_filled_array[3]=3;
non_pre_filled_array[4]=4;
non_pre_filled_array[5]=5;
non_pre_filled_array[6]=6;
non_pre_filled_array[7]=7;
non_pre_filled_array[8]=8;
non_pre_filled_array[9]=9;

5. pre_filled_array[i] (7.10%): 5. pre_filled_array [i](7.10%):

Then to the loops. 然后到循环。 Fastest looping method in this test. 这个测试中最快的循环方法。 The array was preinitialized and prefilled. 阵列被预先初始化并预先填充。

The speed drop compared to inline version (test 3) is 64,44 %. 与内联版本(测试3)相比,速度下降为64.44%。 This is so remarkable difference that I would say, do not loop if not needed. 这是非常显着的差异,我想说,如果不需要,不要循环。 If array size is small (don't know how small, it have to be tested separately), using inline assignments instead of looping are wiser. 如果数组大小很小(不知道它有多小,它必须单独测试),使用内联赋值而不是循环是更明智的。

And because the speed drop is so huge and we really need loops, it's is wise to find better looping method (eg. while(i--) ). 而且因为速度下降是如此巨大而且我们确实需要循环,所以找到更好的循环方法 (例如while(i--) )是明智的。

The test code is here: 测试代码在这里:

for(var i=0;i<10;i++)
{
  pre_filled_array[i]=i;
}

6. non_pre_filled_array[i] (5.26%): 6. non_pre_filled_array [i](5.26%):

If we do not preinitialize and prefill array, the speed decreases 25,96 %. 如果我们不预先初始化和预填充阵列,速度会降低25,96%。 Again, preinitializing and/or prefilling before speed critical loops is wise. 同样,在速度关键循环之前预先初始化和/或预填充是明智的。

The code is here: 代码在这里:

for(var i=0;i<10;i++) 
{
  non_pre_filled_array[i]=i;
}

7. Math calculations (1.17%): 7.数学计算(1.17%):

Every test have to be some reference point. 每个测试都必须是一些参考点。 Mathematical functions are considered slow. 数学函数被认为是缓慢的。 The test consisted of ten "heavy" Math calculations, but now comes the other thing that struck me in this test. 测试包括十次“重”数学计算,但现在又出现了另一件令我印象深刻的测试结果。 Look at speed of 8 and 9 where we push ten integer numbers to array in loop. 看看8和9的速度,我们将十个整数推入循环中的数组。 Calculating these 10 Math functions is more than 30% faster than pushing ten integers into array in loop. 计算这10个Math函数比将10个整数推入循环数组快30%以上。 So, may be it's easier to convert some array pushes to preinitialized non-arrays and keep those trigonometrics. 因此,可能更容易将一些数组推送转换为预初始化的非数组并保留这些三角函数。 Of course if there are hundred or thousands of calculations per frame, it's wise to use eg. 当然,如果每帧有数百或数千个计算,那么使用例如是明智的。 sqrt instead of sin/cos/tan and use taxicab distances for distance comparisons and diamond angles (t-radians) for angle comparisons , but still the main bottleneck can be elsewhere: looping is slower than inlining, pushing is slower than using direct assignment with preinitilization and/or prefilling, code logic, drawing algorithms and DOM access can be slow. sqrt而不是sin / cos / tan并且使用出租车距离进行距离比较和钻石角度(t弧度)进行角度比较 ,但仍然是主要的瓶颈可能在其他地方:循环比内联慢,推动比使用直接赋值慢预处理和/或预填充,代码逻辑,绘图算法和DOM访问可能很慢。 All cannot be optimized in Javascript (we have to see something on the screen!) but all easy and significant we can do, is wise to do. 所有这些都无法在Javascript中进行优化(我们必须在屏幕上看到一些东西!)但是我们能做到的所有简单而重要的事情都是明智之举。 Someone here in SO has said that code is for humans and readable code is more essential than fast code, because maintenance cost is the biggest cost. SO中的某个人说,代码是针对人类的,可读代码比快速代码更重要,因为维护成本是最大的成本。 This is economical viewpoint, but I have found that code optimizing can get the both: elegance and readability and the performance. 这是经济的观点,但我发现代码优化可以获得两者:优雅和可读性以及性能。 And if 5% performance boost is achieved and the code is more straightforwad, it gives a good feeling! 如果达到5%的性能提升并且代码更直接,它会给人一种良好的感觉!

The code is here: 代码在这里:

non_array_0=Math.sqrt(10435.4557);
non_array_1=Math.atan2(12345,24869);
non_array_2=Math.sin(35.345262356547);
non_array_3=Math.cos(232.43575432);
non_array_4=Math.tan(325);
non_array_5=Math.asin(3459.35498534536);
non_array_6=Math.acos(3452.35);
non_array_7=Math.atan(34.346);
non_array_8=Math.pow(234,222);
non_array_9=9374.34524/342734.255;

8. pre_filled_array.push(i) (0.8%): 8. pre_filled_array.push(i)(0.8%):

Push is evil! 推是邪恶的! Push combined to loop is baleful evil! 推动组合循环是恶毒的邪恶! This is for some reason very slow method to assign values into array. 这是由于某种原因将值分配给数组的非常慢的方法。 Test 5 (direct assignments in loop), is nearly 9 times faster than this method and both methods does exactly the same thing: assign integer 0-9 into preinitialized and prefilled array. 测试5(循环中的直接赋值)比这种方法快近9倍,并且两种方法完全相同:将整数0-9分配给预初始化和预填充数组。 I have not tested if this push-for-loop evilness is due to pushing or looping or the combination of both or the looping count. 我还没有测试过这种推送循环的恶意是由于推送或循环还是两者的组合或循环计数。 There are in JSPERF.COM other examples that gives conflicting results. 在JSPERF.COM中有其他示例会产生相互矛盾的结果。 It's wiser to test just with the actual data and make decisions. 用实际数据进行测试并做出决策是明智之举。 This test may not be compatible with other data than what was used. 此测试可能与使用的其他数据不兼容。

And here is the code: 以下是代码:

for(var i=0;i<10;i++)
{
  pre_filled_array.push(i);
}

9. non_pre_filled_array.push(i) (0.74%): 9. non_pre_filled_array.push(i)(0.74%):

The last and slowest method in this test is the same as test 8, but the array is not prefilled. 此测试中的最后和最慢的方法与测试8相同,但不预先填充阵列。 A little slower than 9, but the difference is not significant (7.23%). 略慢于9,但差异不显着(7.23%)。 But let's take an example and compare this slowest method to the fastest. 但是让我们举一个例子,将这种最慢的方法与最快的方法进行比较。 The speed of this method is 0.74% of the speed of the method 1, which means that method 1 is 135 times faster than this. 该方法的速度是方法1的速度的0.74%,这意味着方法1比这快135倍。 So think carefully, if arrays are at all needed in particular use case. 所以仔细想想,如果在特定的用例中完全需要数组。 If it is only one or few pushes, the total speed difference is not noticeable, but on the other hand if there are only few pushes, they are very simple and elegant to convert to non-array variables. 如果它只有一次或几次推动,则总速度差异不明显,但另一方面如果只有很少的推动,则转换为非数组变量非常简单和优雅。

This is the code: 这是代码:

for(var i=0;i<10;i++)
{
  non_pre_filled_array.push(i);
}

And finally the obligatory SO question: 最后是强制性的SO问题:

Because the speed difference according to this test seems to be so huge between non-array-variable- assignments and array-assignments, is there any method to get the speed of non-array-variable-assigments and the dynamics of arrays? 因为根据这个测试的速度差异在非数组变量赋值和数组赋值之间似乎是如此巨大,有没有什么方法可以获得非数组变量赋值的速度和数组的动态?

I cannot use var variable_$i = 1 in a loop so that $i is converted to some integer. 我不能在循环中使用var variable_$i = 1 ,以便将$ i转换为某个整数。 I have to use var variable[i] = 1 which is significantly slower than var variable1 = 1 as the test proved. 我必须使用var variable[i] = 1 ,这比测试证明的var variable1 = 1要慢得多。 This may be critical only when there are large arrays and in many cases they are. 只有存在大型阵列且在很多情况下它们才有用,这可能是至关重要的。


EDIT: I made a new test to confirm the slowness of arrays access and tried to find faster way: 编辑:我做了一个新的测试,以确认数组访问的缓慢,并试图找到更快的方法:

http://jsperf.com/read-write-array-vs-variable http://jsperf.com/read-write-array-vs-variable

Array-read and/or array-write are significantly slower than using normal variables. 数组读取和/或数组写入比使用普通变量要慢得多。 If some operations are done to array members, it's wiser to store the array member value to a temp variable, make those operations to temp variable and finally store the value into the array member. 如果对数组成员执行某些操作,则将数组成员值存储到临时变量更明智,将这些操作设置为temp变量,最后将值存储到数组成员中。 And although code becomes larger, it's significantly faster to make those operations inline than in loop. 虽然代码变得越来越大,但是使这些操作内联比使用循环要快得多。

Conclusion: arrays vs normal variables are analogous to disk vs memory. 结论:数组与正常变量类似于磁盘与内存。 Usually memory access is faster than disk access and normal variables access is faster than array access. 通常,内存访问比磁盘访问更快,而正常变量访问比数组访问更快。 And may be concatenating operations is also faster than using intermediate variables, but this makes code a little non readable. 并且可能连接操作也比使用中间变量更快,但这使代码有点不可读。


Assigning values to arrays is significantly slower than assigning to normal variables. 为数组赋值比分配给常规变量要慢得多。 Arrays are evil! 阵列是邪恶的! This is very sad and I really don't understand why this occurs. 这非常难过,我真的不明白为什么会这样。 Arrays are so important! 数组非常重要!

That's because normal variables are statically scoped and can be (and are) easily optimised. 这是因为正常变量是静态范围的,并且可以(并且)可以轻松优化。 The compiler/interpreter will learn their type, and might even avoid repeated assignments of the same value. 编译器/解释器将学习它们的类型,甚至可以避免重复分配相同的值。

These kind of optimisations will be done for arrays as well, but they're not so easy and will need longer to take effect. 这些优化也将针对数组进行,但它们并不那么容易,需要更长时间才能生效。 There is additional overhead when resolving the property reference, and since JavaScript arrays are auto-growing lists the length needs to be checked as well. 解析属性引用时会有额外的开销,并且由于JavaScript数组是自动增长列表,因此也需要检查长度。

Prepopulating the arrays will help to avoid reallocations for capacity changes, but for your little arrays ( length =10) it shouldn't make much difference. 预先填充数组将有助于避免容量更改的重新分配,但对于您的小数组( length = 10),它应该没有太大的区别。

Is there any method to get the speed of non-array-variable-assigments and the dynamics of arrays? 有没有什么方法可以获得非数组变量分配的速度和数组的动态?

No. Dynamics do cost, but they are worth it - as are loops. 不,动力学确实有成本,但它们是值得的 - 就像循环一样。

You hardly ever will be in the case to need such a micro-optimisation, don't try it . 你几乎不需要这样的微优化, 不要尝试它 The only thing I can think of are fixed-sized loops (n <= 4) when dealing with ImageData , there inlining is applicable. 在处理ImageData ,我唯一能想到的是固定大小的循环(n <= 4),内联是适用的。

Push is evil! 推是邪恶的!

Nope, only your test was flawed. 不,只有你的测试有缺陷。 The jsperf snippets are executed in a timed loop without tearup and -down, and only there you have been resetting the size. jsperf片段在定时循环中执行,没有撕下和向下,只有你重置大小。 Your repeated push es have been producing arrays with lengths of hundredth thousands, with correspondent need of memory (re-)allocations. 你的重复push已经产生了长度为数十万的数组,并且需要相应的内存(重新)分配。 See the console at http://jsperf.com/pre-filled-array/11 . 请访问http://jsperf.com/pre-filled-array/11上的控制台。

Actually push is just as fast as property assignment. 实际上push与财产分配一样快。 Good measurements are rare, but those that are done properly show varying results across different browser engine versions - changing rapidly and unexpected. 良好的测量很少,但那些正确完成的测量显示不同浏览器引擎版本的不同结果 - 快速变化和意外。 See How to append something to an array? 请参见如何将某些内容附加到数组中? , Why is array.push sometimes faster than array[n] = value? 为什么array.push有时比array [n] = value更快? and Is there a reason JavaScript developers don't use Array.push()? 有没有理由JavaScript开发人员不使用Array.push()? - the conclusion is that you should use what is most readable / appropriate for your use case, not what you think could be faster. - 结论是你应该使用最适合你的用例的东西,而不是你认为更快的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM