我知道Math.abs()和Math.round()是非常不同的函数,但我认为它们的效率会相对相似。
console.time('round');
for (var i = 0; i < 100000; i++) {
var a = Math.random() - 0.5;
Math.round(a);
}
console.timeEnd('round');
console.time('abs');
for (var i = 0; i < 100000; i++) {
var a = Math.random() - 0.5;
Math.abs(a);
}
console.timeEnd('abs');
之前产生了以下结果: 回合:136.435ms abs:4777.983ms
任何人都可以解释时间上的根本区别吗?
编辑:当我在这里运行snippit时,我获得了更快的结果。大约2和3毫秒。为什么它会在不同的标签中获得如此高的时间呢?
谢谢!
答案 0 :(得分:1)
我不确定您是在衡量自己的想法
随机可以通过缓存丢失来解决问题。此外,您通过0.5
进行减法,这也是 FPU 操作,它比abs
本身复杂得多。我不是JAVA编码器,但我希望Math.abs(x)
是浮点而不是整数运算(在C / C ++中abs
是整数而fabs
是浮点数)。我会创建在循环之前设置随机数的数组,然后在循环中使用它
<强> abs
强>
abs
只是符号位测试+非零尾数测试。如果实施包含早午餐,那么可能会严重减缓事情。幸运的是,float / double abs实现不需要任何早午餐,只需掩盖符号位(因为尾数不是标准IEEE 754格式的2'补码)。
<强> round
强>
round
是测试尾数的小数部分的MSB是否为1,如果是,则应用整数增量。所以目标被移位到整数,并提取MSB分数位。如果实现包含早午餐然后可以严重减慢事情,但通常更快是将MSB分数位提取到Carry标志并使用adc
。仍然需要比abs
更多的工作,所以它应该更慢。
为什么结果如此?
您的实施/平台是使用FPU还是软件模拟?在FPU上,两种操作的复杂性几乎相同(因为与FPU通信的开销通常比这种操作本身更大)。在仿真时,它取决于操作和目标平台体系结构的实现(管道,缓存控制,......)
我的猜测是:abs
循环更糟糕abs
实施并没有那么多优化,因为它可能round
由编译器进行优化,有时round(x)=floor(a+0.5)
并且您之前有a-=0.5;
,因为您不使用a
进行任何其他操作,编译器可能会忽略{ {1}}并直接使用floor(random-0.5+0.5)
<强> [注释] 强>
你测量132ms和4.7sec的时间对于你试试的硬件来说太大了?如今,编辑的时间对于普通的PC硬件和代码解释器来说更合理。你有多次测量这个吗?
如果你在布朗瑟中尝试这个,那么它可能会被其背景中的任何东西放慢速度(比如从不同的页面剪切或仍然下载的东西......)OS也可以暂停执行但不是那么多
答案 1 :(得分:1)
不是答案,而是一点研究。
V8引擎来源(https://chromium.googlesource.com/v8/v8.git)我在src/math.js
<强>的Math.random 强>
function MathRound(x) {
return %RoundNumber(TO_NUMBER_INLINE(x));
}
%RoundNumber
应该引用src/runtime/runtime-maths.cc
RUNTIME_FUNCTION(Runtime_RoundNumber) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_NUMBER_ARG_HANDLE_CHECKED(input, 0);
isolate->counters()->math_round()->Increment();
if (!input->IsHeapNumber()) {
DCHECK(input->IsSmi());
return *input;
}
Handle<HeapNumber> number = Handle<HeapNumber>::cast(input);
double value = number->value();
int exponent = number->get_exponent();
int sign = number->get_sign();
if (exponent < -1) {
// Number in range ]-0.5..0.5[. These always round to +/-zero.
if (sign) return isolate->heap()->minus_zero_value();
return Smi::FromInt(0);
}
// We compare with kSmiValueSize - 2 because (2^30 - 0.1) has exponent 29 and
// should be rounded to 2^30, which is not smi (for 31-bit smis, similar
// argument holds for 32-bit smis).
if (!sign && exponent < kSmiValueSize - 2) {
return Smi::FromInt(static_cast<int>(value + 0.5));
}
// If the magnitude is big enough, there's no place for fraction part. If we
// try to add 0.5 to this number, 1.0 will be added instead.
if (exponent >= 52) {
return *number;
}
if (sign && value >= -0.5) return isolate->heap()->minus_zero_value();
// Do not call NumberFromDouble() to avoid extra checks.
return *isolate->factory()->NewNumber(Floor(value + 0.5));
}
<强> Math.abs 强>
function MathAbs(x) {
x = +x;
return (x > 0) ? x : 0 - x;
}
Node.JS使用V8 enginge,Chrome也是如此
我的测试用例:
var randoms = [];
for (var i = 0; i < 100000; i++) {
randoms.push(Math.random() - 0.5);
}
for(var r = 0; r < randoms.length; r++) {
console.time('round');
for (var i = 0; i < randoms.length; i++) {
Math.round(randoms[i]);
}
console.timeEnd('round');
console.time('abs');
for (var i = 0; i < randoms.length; i++) {
Math.abs(randoms[i]);
}
console.timeEnd('abs');
}
结果:
<强>思想强>
通过V8源代码,我希望Math.abs
更快,它在Node.JS中,但不在Chrome中。
想法为什么?