繁体   English   中英

为数组赋值的性能

[英]Performance of assigning values to array

代码优化在这里说,分析是优化javascript的第一步,建议的引擎是Chrome和Firefox的分析器。 这些问题是他们以某种奇怪的方式告诉每个函数执行的时间,但我对它们没有很好的理解。 最有用的方法是分析器会告诉每行执行的次数,以及每行可能花费的时间。 这样就可以严格地看到瓶颈。 但在实施/找到此类工具之前,我们有两种选择:

1)制作自己的计算器,计算某个代码块或行执行的时间和次数2)学会理解哪些是慢速方法,哪些不是

对于选项2, jsperf.com非常有帮助。 我试图学习优化数组并在JSPERF.COM中进行速度测试。 下图显示了5个主要浏览器的结果,发现了一些我之前不知道的瓶颈。

速度测试

主要调查结果如下:

1)尽管使用哪种方法进行分配,但为数组分配值要比分配给正常变量要慢得多。

2)在性能关键循环之前预初始化和/或预填充阵列可以显着提高速度

3)与将数值推入数组相比,数学三角函数不是那么慢(!)

以下是每项测试的解释:


1. non_array(100%):

变量以这种方式给出了预定义的值:

var non_array_0=0;
var non_array_1=0;
var 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;

上面是一个类似数组的变量,但似乎无法以其他方式迭代或引用这些变量作为对阵列的反对。 还是有吗?

此测试中的任何内容都不比为变量赋值更快。


2. non_array_non_pre(83.78%)

与测试1完全相同,但变量未预先初始化或预先填充。 速度是测试速度的83.78%。在每个测试的浏览器中,预填充变量的速度比未预先填充的速度快。 因此,在任何速度关键循环之外初始化(并可能预填充)变量。

测试代码在这里:

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)并将数组放入图片时,速度会显着降低。 虽然我们进行了所有优化(预初始化和预填充数组),然后直接分配值而不进行循环或推送,但速度降低到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的测试相同,但是数组成员不是预先初始化的,也不是预先var non_pre_filled_array=[]; ,只有优化是事先初始化数组: var non_pre_filled_array=[];

与预先确定的测试3相比,速度降低了58,23%。 因此预先初始化和/或预填充阵列使速度加倍。

测试代码在这里:

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

然后到循环。 这个测试中最快的循环方法。 阵列被预先初始化并预先填充。

与内联版本(测试3)相比,速度下降为64.44%。 这是非常显着的差异,我想说,如果不需要,不要循环。 如果数组大小很小(不知道它有多小,它必须单独测试),使用内联赋值而不是循环是更明智的。

而且因为速度下降是如此巨大而且我们确实需要循环,所以找到更好的循环方法 (例如while(i--) )是明智的。

测试代码在这里:

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

6. non_pre_filled_array [i](5.26%):

如果我们不预先初始化和预填充阵列,速度会降低25,96%。 同样,在速度关键循环之前预先初始化和/或预填充是明智的。

代码在这里:

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

7.数学计算(1.17%):

每个测试都必须是一些参考点。 数学函数被认为是缓慢的。 测试包括十次“重”数学计算,但现在又出现了另一件令我印象深刻的测试结果。 看看8和9的速度,我们将十个整数推入循环中的数组。 计算这10个Math函数比将10个整数推入循环数组快30%以上。 因此,可能更容易将一些数组推送转换为预初始化的非数组并保留这些三角函数。 当然,如果每帧有数百或数千个计算,那么使用例如是明智的。 sqrt而不是sin / cos / tan并且使用出租车距离进行距离比较和钻石角度(t弧度)进行角度比较 ,但仍然是主要的瓶颈可能在其他地方:循环比内联慢,推动比使用直接赋值慢预处理和/或预填充,代码逻辑,绘图算法和DOM访问可能很慢。 所有这些都无法在Javascript中进行优化(我们必须在屏幕上看到一些东西!)但是我们能做到的所有简单而重要的事情都是明智之举。 SO中的某个人说,代码是针对人类的,可读代码比快速代码更重要,因为维护成本是最大的成本。 这是经济的观点,但我发现代码优化可以获得两者:优雅和可读性以及性能。 如果达到5%的性能提升并且代码更直接,它会给人一种良好的感觉!

代码在这里:

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

推是邪恶的! 推动组合循环是恶毒的邪恶! 这是由于某种原因将值分配给数组的非常慢的方法。 测试5(循环中的直接赋值)比这种方法快近9倍,并且两种方法完全相同:将整数0-9分配给预初始化和预填充数组。 我还没有测试过这种推送循环的恶意是由于推送或循环还是两者的组合或循环计数。 在JSPERF.COM中有其他示例会产生相互矛盾的结果。 用实际数据进行测试并做出决策是明智之举。 此测试可能与使用的其他数据不兼容。

以下是代码:

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

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

此测试中的最后和最慢的方法与测试8相同,但不预先填充阵列。 略慢于9,但差异不显着(7.23%)。 但是让我们举一个例子,将这种最慢的方法与最快的方法进行比较。 该方法的速度是方法1的速度的0.74%,这意味着方法1比这快135倍。 所以仔细想想,如果在特定的用例中完全需要数组。 如果它只有一次或几次推动,则总速度差异不明显,但另一方面如果只有很少的推动,则转换为非数组变量非常简单和优雅。

这是代码:

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

最后是强制性的SO问题:

因为根据这个测试的速度差异在非数组变量赋值和数组赋值之间似乎是如此巨大,有没有什么方法可以获得非数组变量赋值的速度和数组的动态?

我不能在循环中使用var variable_$i = 1 ,以便将$ i转换为某个整数。 我必须使用var variable[i] = 1 ,这比测试证明的var variable1 = 1要慢得多。 只有存在大型阵列且在很多情况下它们才有用,这可能是至关重要的。


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

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

数组读取和/或数组写入比使用普通变量要慢得多。 如果对数组成员执行某些操作,则将数组成员值存储到临时变量更明智,将这些操作设置为temp变量,最后将值存储到数组成员中。 虽然代码变得越来越大,但是使这些操作内联比使用循环要快得多。

结论:数组与正常变量类似于磁盘与内存。 通常,内存访问比磁盘访问更快,而正常变量访问比数组访问更快。 并且可能连接操作也比使用中间变量更快,但这使代码有点不可读。


为数组赋值比分配给常规变量要慢得多。 阵列是邪恶的! 这非常难过,我真的不明白为什么会这样。 数组非常重要!

这是因为正常变量是静态范围的,并且可以(并且)可以轻松优化。 编译器/解释器将学习它们的类型,甚至可以避免重复分配相同的值。

这些优化也将针对数组进行,但它们并不那么容易,需要更长时间才能生效。 解析属性引用时会有额外的开销,并且由于JavaScript数组是自动增长列表,因此也需要检查长度。

预先填充数组将有助于避免容量更改的重新分配,但对于您的小数组( length = 10),它应该没有太大的区别。

有没有什么方法可以获得非数组变量分配的速度和数组的动态?

不,动力学确实有成本,但它们是值得的 - 就像循环一样。

你几乎不需要这样的微优化, 不要尝试它 在处理ImageData ,我唯一能想到的是固定大小的循环(n <= 4),内联是适用的。

推是邪恶的!

不,只有你的测试有缺陷。 jsperf片段在定时循环中执行,没有撕下和向下,只有你重置大小。 你的重复push已经产生了长度为数十万的数组,并且需要相应的内存(重新)分配。 请访问http://jsperf.com/pre-filled-array/11上的控制台。

实际上push与财产分配一样快。 良好的测量很少,但那些正确完成的测量显示不同浏览器引擎版本的不同结果 - 快速变化和意外。 请参见如何将某些内容附加到数组中? 为什么array.push有时比array [n] = value更快? 有没有理由JavaScript开发人员不使用Array.push()? - 结论是你应该使用最适合你的用例的东西,而不是你认为更快的东西。

暂无
暂无

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

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