数组赋值的性能 [英] Performance of assigning values to array

查看:173
本文介绍了数组赋值的性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

code优化这里说,这样分析是优化的JavaScript的第一步,建议引擎是Chrome和Firefox的剖析。这些问题是他们的一些不可思议的方式告诉大家,每次执行功能的时间,但我还没有得到他们任何很好的理解。最有用的方法是,该分析器会告诉,多少次被执行的每一行,如果以往任何时候都可能也是花费在每行的时间。这样才有可能看到瓶颈严格。但实现这样的工具前/发现,我们有两个选择:

1)做自己的小算盘,计数的时间和多少次某些code座或行执行
2)学会区分哪些是缓慢的方法,哪些不是。

对于选择2 jsperf.com 是很大的帮助。我曾试图学习优化阵列,并取得了 JSPERF.COM 速度测试。下图显示了在5个主要的浏览器的结果,并发现了一些瓶颈,我不知道前面

主要研究结果:

1)指定值数组比分配给尽管其中用于分配方法正常变量显著慢。

2)preinitializing和/或prefilling前阵性能的关键循环可以显著提高速度

3)数学三角函数都没有这么慢相比,推值代入阵列(!)

下面是每个测试说明:


1。 non_array(100%):

变量分别获得了predefined价值是这样的:

  VAR non_array_0 = 0;
变种non_array_1 = 0;
变种non_array_2 = 0;
...

和在定时地区,他们被称为是这样的:

  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;

以上是一个类似数组的变量,但似乎有没有办法来迭代或引用这些变量在其他的方式oppocite数组。还是有?

在这个测试中,没有什么比分配数量可变的速度更快。


2。 non_array_non_ pre(83.78%)

完全一样的测试1,但这些变量并没有pre-初始化,也不prefilled。速度是试验1的速度在每一个测试器prefilled变量的速度比非prefilled更快的83,78%。 所以初始化(也可能是pre灌注)变量以外的任何速度关键回路。

测试code是在这里:

  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%):

数组是邪恶的!:当我们丢掉正常的变量(TEST1和TEST2),并采取阵列中的图片,速度显著下降。 尽管我们尽了所有优化(preinitialize和pre灌注数组),然后直接赋值不用循环或推,速度降低到19.96%左右。这是非常可悲的,我真不'不懂为什么发生这种情况。这是在该试验的主要冲击箱中的一个。数组是如此重要,我还没有找到一种方法,使许多事情没有数组。

测试数据是在这里:

  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%):

这是相同的测试为3,但数组成员不是preinitialized也不prefilled,只有优化是初始化数组事先: VAR non_ pre_filled_array = [ ];

相比,preinitilized测试速度下降58,23%3. 所以preinitializing和/或prefilling阵列上增加一倍的速度。

测试code是在这里:

  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%):

接着向环路。最快的循环在这种测试方法。该阵列preinitialized和prefilled。

的速度下降相比线版本(试验3)为64,44%。这是如此显着的区别,我会说,如果没有必要不要循环。如果数组大小小(不知道有多小,它需要单独测试),使用内联任务,而不是循环是明智的。

和因为速度下降是如此巨大,我们真正需要的循环,这是明智的做法是找到更好的循环方法(例如:而(I - )。

测试code是在这里:

 为(VAR I = 0;我小于10;我++)
{
  pre_filled_array [我] =我;
}


6。 non_ pre_filled_array [I](5.26%):

如果我们不preinitialize和pre灌注阵列,速度下降25,96%。前速关​​键回路同样,preinitializing和/或prefilling是明智的。

在code是在这里:

 为(VAR I = 0;我小于10;我++)
{
  non_ pre_filled_array [我] =我;
}


7。数学计算(1.17%):

每一次测试都必须有一定的参考点。数学函数被认为是缓慢的。测试包括十重数学计算,但现在来袭击我在这个测试中的其他事情。看看8和9的速度,我们在循环推10整数数组。计算这些10数学函数比推10整数到数组中循环快30%以上。因此,可能会更容易将一些阵列推向preinitialized非阵列,保留那些trigonometrics。当然,如果有一百个或数千个每帧计算,明智的做法是使用如。而不是正弦/余弦/棕褐色和使用出租车的距离为距离比较和钻石角(T-弧度)的角度比较开方的,但仍是主要瓶颈可能是在别处:循环比内联慢,推比使用与preinitilization和/或prefilling,code逻辑,绘制算法和访问DOM直接赋值可能会很慢慢。所有不能在Javascript的优化(我们要在屏幕上看到的东西!),但所有容易,我们可以做的显著,是明智的事情。有人在这里SO曾表示,code是人类可读code比快速code更重要,因为维护成本是最大的成本。这是经济的观点,但我发现,code优化可以得到两个:优雅和可读性和性能。如果5%的性能提升,实现与code更straightforwad,给人一种美好的感觉!

在code是在这里:

  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(234222);
non_array_9 = 9374.34524 / 342734.255;


8。 pre_filled_array.push(I)(0.8%):

推是邪恶的!推相结合,循环恶意邪恶!这是因为某些原因很慢的方法来赋值到数组。测试5(环路直接分配),比这个方法快近9倍,这两种方法做同样的事情:整型的0-9到preinitialized和prefilled阵列。如果此推for循环恶是由于推力或循环或两者的组合或循环计数我没有测试。有在该给出矛盾的结果JSPERF.COM其它实例。这是明智的做法只是测试与实际数据和做出决策。这个测试可能不会比使用什么其它数据相兼容。

这里是code:

 为(VAR I = 0;我小于10;我++)
{
  pre_filled_array.push(ⅰ);
}


9。 non_ pre_filled_array.push(I)(0.74%):

在本试验中的最后和最慢的方法是相同的试验8,但数组不是prefilled。超过900慢一点,但差别并不显著(7.23%)。但是,让我们举个例子,这最慢的方法比较,以最快的。 此方法的速度是方法1,这意味着,方法1比这个快135倍的速度的0.74%。所以仔细一想,如果在所有需要特定用例的阵列。如果只有一个或几个推,总的速度差异并不明显,但如果只有少数推另一方面,他们是非常简单而优雅转换到非数组变量。

这是code:

 为(VAR I = 0;我小于10;我++)
{
  non_ pre_filled_array.push(ⅰ);
}


最后强制性的SO问题:

因为按照本次测试的速度差似乎非数组可变分配和阵列的任务之间如此之大,有没有方法来获取非数组变量,assigments和速度阵列的动态?

我不能使用 VAR variable_ $ I = 1 在一个循环,这样$ I转化为某个整数。我必须使用 var变量。[I] = 1 VAR变量1 = 1 显著慢作为试验证明。只有当有大的阵列,这可能是关键的,而且在许多情况下,它们是


编辑:
我做了一个新的测试,以确认数组访问缓慢,并试图找到更快的方法:

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

阵列的读取和/或阵列的写比使用正常变量显著慢。如果某些操作完成数组成员,这是明智的数组成员值存储到一个临时变量,做出临时变量的操作,最后存储值到数组成员。虽然code变大,这是显著更快地使这些操作直列比循环。

结论:阵列与正常变量类似于磁盘VS内存。通常内存访问比磁盘访问和正常变量访问比数组访问速度更快。并可以串联操作也比使用中间变量快,但是这使得code有点不可读。



解决方案

  

指定值数组比分配到正常的变量显著慢。数组是邪恶的!这是非常可悲的,我真的不明白为什么发生这种情况。数组是如此的重要!


这是因为正常的变量是静态范围,可以是(而且)很容易优化。编译器/间preTER将学习它们的类型,甚至可能避免相同值的重复任务。

这样的优化将会对数组进行为好,但他们不是那么容易的,需要更长的时间才能生效。有额外的开销解析属性引用的时候,而且由于JavaScript数组是自动增长的清单需要检查以及长度。

prepopulating阵列将有助于避免对能力的变化重新分配,但对你的小阵列(长度 = 10),应该没有太大的差别。


  

有没有什么方法来获得非数组可变assigments的速度和阵列的动态?


没有。动力做成本,但他们是值得的 - 因为是循环

您很少会在情况需要这样的微型优化,不试试吧。我能想到的唯一的事情是固定大小的循环(N< = 4)。用的ImageData 打交道时,有内联适用


  

推是邪恶的!


没有,只有你的测试是有缺陷的。该jsperf片段是在定时循环不tearup和-down执行,只有在那里你被重置大小。您反复 ES已经生产阵列与百上千的长度,与记者需要的内存(重新)分配。看到控制台在 http://jsperf.com/$p$p-filled-array / 11

实际上是一样快的属性赋值。良好的三围是罕见的,但那些做正确显示在不同的浏览器引擎的版本不同的结果 - 快速和意外的变化。请参见追加数组为什么是的Array.push有时比数组[n] =值更快?是否有一个原因,JavaScript开发人员都使用数组.push() - ?结论是,你应该使用什么是最可读/适合您使用的情况下,不是你想的可能会更快

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. 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

For option 2 jsperf.com is of great help. I have tried to learn optimizing arrays and made a speed test in JSPERF.COM. The following image shows the results in 5 main browsers and found some bottlenecks that I didn't know earlier.

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.

2) Preinitializing and/or prefilling array before performance critical loops can improve speed significantly

3) Math trigonometric functions are not so slow when compared to pushing values into arrays(!)

Here are the explanations of every test:


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%)

Exactly the same as test 1, but the variables were not pre-initialized nor prefilled. 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. 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 %):

Arrays are evil! When we throw away normal variables (test1 and test2) and take arrays in to the picture, the speed decreases significantly. 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. 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%):

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=[];

The speed decreases 58,23 % compared to preinitilized test 3. So preinitializing and/or prefilling array over doubles the speed.

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%):

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 %. 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--)).

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%):

If we do not preinitialize and prefill array, the speed decreases 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%):

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. Calculating these 10 Math functions is more than 30% faster than pushing ten integers into array in loop. 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. 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. 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. 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!

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%):

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. 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. 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%):

The last and slowest method in this test is the same as test 8, but the array is not prefilled. A little slower than 9, but the difference is not significant (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. 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:

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. I have to use var variable[i] = 1 which is significantly slower than var variable1 = 1 as the test proved. 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

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. 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.

Prepopulating the arrays will help to avoid reallocations for capacity changes, but for your little arrays (length=10) it shouldn't make much difference.

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.

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. Your repeated pushes have been producing arrays with lengths of hundredth thousands, with correspondent need of memory (re-)allocations. See the console at http://jsperf.com/pre-filled-array/11.

Actually push is just as fast as property assignment. Good measurements are rare, but those that are done properly show varying results across different browser engine versions - changing rapidly and unexpected. See Appending to array, Why is array.push sometimes faster than array[n] = value? and Is there a reason JavaScript developers don't use 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.

这篇关于数组赋值的性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆