gpt4 book ai didi

javascript - 为什么预计算 sin(x) *比在 Javascript 中使用 Math.sin() *慢*?

转载 作者:数据小太阳 更新时间:2023-10-29 05:28:33 29 4
gpt4 key购买 nike

我在 JavaScript 中发现了一个有趣的异常现象。其中重点是我尝试通过预先计算 sin(x) 和 cos(x) 并简单地引用预先计算的值来加速三 Angular 变换计算。

直觉上,预计算比每次计算 Math.sin() 和 Math.cos() 函数更快。特别是如果您的应用程序设计将仅使用一组受限制的值作为三 Angular 函数的参数(在我的例子中,区间 [0°, 360°] 中的整数度),这足以满足我的目的。

所以,我进行了一些测试。在预先计算 sin(x) 和 cos(x) 的值并将它们存储在 360 个元素的数组中之后,我编写了一个简短的测试函数,通过一个简单的测试 HTML 页面中的按钮激活,以比较两者的速度方法。一个循环简单地将一个值乘以预先计算的数组元素值,而另一个循环将一个值乘以 Math.sin()。

我的预期是预先计算的循环明显快于涉及对 trig 函数的函数调用的循环。令我惊讶的是,预先计算的循环较慢

这是我写的测试函数:

function MyTest()
{
var ITERATION_COUNT = 1000000;

var angle = Math.floor(Math.random() * 360);

var test1 = 200 * sinArray[angle];

var test2 = 200 * cosArray[angle];

var ref = document.getElementById("Output1");

var outData = "Test 1 : " + test1.toString().trim() + "<br><br>";
outData += "Test 2 : "+test2.toString().trim() + "<br><br>";

var time1 = new Date(); //Time at the start of the test

for (var i=0; i<ITERATION_COUNT; i++)
{
var angle = Math.floor(Math.random() * 360);
var test3 = (200 * sinArray[angle]);

//End i loop
}

var time2 = new Date();

//This somewhat unwieldy procedure is how we find out the elapsed time ...

var msec1 = (time1.getUTCSeconds() * 1000) + time1.getUTCMilliseconds();
var msec2 = (time2.getUTCSeconds() * 1000) + time2.getUTCMilliseconds();

var elapsed1 = msec2 - msec1;

outData += "Test 3 : Elapsed time is " + elapsed1.toString().trim() + " milliseconds<br><br>";

//Now comparison test with the same number of sin() calls ...

var time1 = new Date();

for (var i=0; i<ITERATION_COUNT; i++)
{
var angle = Math.floor(Math.random() * 360);
var test3 = (200 * Math.sin((Math.PI * angle) / 180));

//End i loop
}

var time2 = new Date();

var msec1 = (time1.getUTCSeconds() * 1000) + time1.getUTCMilliseconds();
var msec2 = (time2.getUTCSeconds() * 1000) + time2.getUTCMilliseconds();

var elapsed2 = msec2 - msec1;

outData += "Test 4 : Elapsed time is " + elapsed2.toString().trim() + " milliseconds<br><br>";

ref.innerHTML = outData;

//End function
}

我这样做的动机是,乘以从数组中获取的预先计算的值比调用触发函数的函数调用更快,但我获得的结果很奇怪。

一些示例运行产生以下结果(测试 3 是预先计算的运行时间,测试 4 是 Math.sin() 运行时间):

运行 1:

Test 3 : Elapsed time is 153 milliseconds

Test 4 : Elapsed time is 67 milliseconds

运行 2:

Test 3 : Elapsed time is 167 milliseconds

Test 4 : Elapsed time is 69 milliseconds

运行 3:

Test 3 : Elapsed time is 265 milliseconds

Test 4 : Elapsed time is 107 milliseconds

运行 4:

Test 3 : Elapsed time is 162 milliseconds

Test 4 : Elapsed time is 69 milliseconds

为什么调用三 Angular 函数的速度是引用数组中预计算值的两倍,而预计算方法至少在直觉上应该更快一些?更重要的是,因为我在预先计算的循环中使用整数参数来索引数组,而函数调用循环还包括一个额外的计算来将度数转换为弧度?

这里发生了一些有趣的事情,但目前我不确定是什么。通常,数组访问预先计算的数据比调用复杂的三 Angular 函数要快得多(或者至少,它们回到了我在汇编程序中编写类似代码的时代!),但 JavaScript 似乎颠覆了这一点。我能想到的唯一原因是,JavaScript 在幕后为数组访问增加了很多开销,但如果真是这样,这将影响许多其他代码,这些代码似乎以完全合理的速度运行。

那么,这里到底发生了什么?

我在谷歌浏览器中运行这段代码:

版本 60.0.3112.101(官方构建)(64 位)

在 Windows 7 64 位系统上运行。我还没有在 Firefox 中尝试过,看看是否会出现相同的异常结果,但这是待办事项列表中的下一个。

任何深入了解 JavaScript 引擎内部工作原理的人,请帮忙!

最佳答案

优化器扭曲了结果。

两个相同的测试函数,差不多吧。
在基准测试中运行它们,结果令人惊讶。

{

func : function (){
var i,a,b;
D2R = 180 / Math.PI
b = 0;
for (i = 0; i < count; i++ ) {
// single test start
a = (Math.random() * 360) | 0;
b += Math.sin(a * D2R);
// single test end
}
},
name : "summed",
},{
func : function (){
var i,a,b;
D2R = 180 / Math.PI;
b = 0;
for (i = 0; i < count; i++ ) {
// single test start
a = (Math.random() * 360) | 0;
b = Math.sin(a * D2R);
// single test end
}
},
name : "unsummed",
},

结果

=======================================
Performance test. : Optimiser check.
Use strict....... : false
Duplicates....... : 4
Samples per cycle : 100
Tests per Sample. : 10000
---------------------------------------------
Test : 'summed'
Calibrated Mean : 173µs ±1µs (*1) 11160 samples 57,803,468 TPS
---------------------------------------------
Test : 'unsummed'
Calibrated Mean : 0µs ±1µs (*1) 11063 samples Invalid TPS
----------------------------------------
Calibration zero : 140µs ±0µs (*)
(*) Error rate approximation does not represent the variance.
(*1) For calibrated results Error rate is Test Error + Calibration Error.
TPS is Tests per second as a calculated value not actual test per second.

基准测试几乎没有为未总结的测试(必须强制它完成)花费任何时间。

优化器知道只需要未求和测试循环的最后一个结果。它仅对最后一次迭代有效,所有其他结果均未使用,所以为什么要使用它们。

javascript 中的基准测试充满陷阱。使用质量基准测试器,了解优化器可以做什么。

罪恶和查找测试。

测试数组和 sin。为了公平起见,我没有将度数转换为弧度。

tests : [{
func : function (){
var i,a,b;
b=0;
for (i = 0; i < count; i++ ) {
a = (Math.random() * 360) | 0;
b += a;
}
},
name : "Calibration",
},{
func : function (){
var i,a,b;
b = 0;
for (i = 0; i < count; i++ ) {
a = (Math.random() * 360) | 0;
b += array[a];

}
},
name : "lookup",
},{
func : function (){
var i,a,b;
b = 0;
for (i = 0; i < count; i++ ) {
a = (Math.random() * 360) | 0;
b += Math.sin(a);
}
},
name : "Sin",
}
],

结果

=======================================
Performance test. : Lookup compare to calculate sin.
Use strict....... : false
Data view........ : false
Duplicates....... : 4
Cycles........... : 1055
Samples per cycle : 100
Tests per Sample. : 10000
---------------------------------------------
Test : 'Calibration'
Calibrator Mean : 107µs ±1µs (*) 34921 samples
---------------------------------------------
Test : 'lookup'
Calibrated Mean : 6µs ±1µs (*1) 35342 samples 1,666,666,667TPS
---------------------------------------------
Test : 'Sin'
Calibrated Mean : 169µs ±1µs (*1) 35237 samples 59,171,598TPS
-All ----------------------------------------
Mean : 0.166ms Totals time : 17481.165ms 105500 samples
Calibration zero : 107µs ±1µs (*);
(*) Error rate approximation does not represent the variance.
(*1) For calibrated results Error rate is Test Error + Calibration Error.
TPS is Tests per second as a calculated value not actual test per second.

再次强制完成,因为查找太接近错误率。但是校准后的查找几乎与时钟速度完美匹配???巧合..我不确定。

关于javascript - 为什么预计算 sin(x) *比在 Javascript 中使用 Math.sin() *慢*?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45896703/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com