繁体   English   中英

为什么Math.abs比Math.round需要更长的时间?

[英]Why does Math.abs take so much longer than Math.round?

我知道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毫秒。 为什么它会在不同的标签中获得如此高的时间?

谢谢!

  1. 我不确定你是在衡量你的想法

    随机可以通过缓存丢失来解决问题。 你也减去0.5 ,这也是FPU操作,它比abs本身复杂得多。 我不是JAVA编码器,但我希望Math.abs(x)是浮点而不是整数运算(在C / C ++中, abs是整数,而fabs是浮点数)。 我会创建在循环之前设置随机数的数组,然后在循环中使用它

  2. abs

    abs只是符号位测试+非零尾数测试。 如果实施包含早午餐,那么可能会严重减缓事情。 幸运的是,浮动/双重abs实现不需要任何早午餐,只需掩盖符号位(因为尾数不是标准IEEE 754格式的2'补码)。

  3. round

    如果尾数的小数部分的MSB是1并且如果是,那么应用整数增量来测试round 所以目标被移位到整数,并提取MSB分数位。 如果实现包含早午餐然后可以严重减慢事情,但通常更快是将MSB分数位提取到Carry标志并使用adc 仍然需要比abs更多的工作,所以它应该更慢。

  4. 为什么结果如此?

    您的实施/平台是使用FPU还是软件仿真? 在FPU上,两种操作的复杂性几乎相同(因为与FPU通信的开销通常比这种操作本身更大)。 在仿真时,它取决于操作和目标平台架构的实现(管道,缓存控制......)

    我的猜测是:

    • 缓存未命中时, abs循环更糟糕
    • abs实现并没有那么多优化
    • round由编译器优化,有时round(x)=floor(a+0.5) ,你有a-=0.5; 之前,当你不使用a为别的有可能编译器会忽略的floor(random-0.5+0.5)并直接使用floor(random)

[笔记]

您测量132ms和4.7sec的时间对于您尝试的HW来说太大了? 如今,编辑的时间对于普通的PC硬件和代码解释器来说更合理。 你测量了这个超过1次吗?

如果你在brownser中尝试这个,那么它可能会被其背景中的任何东西放慢速度(比如从不同的页面剪切或仍在下载某些内容......)OS也可以暂停执行但不是那么多

不是答案,而是一点研究。

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');  
}

结果:

  • Chrome(42.0.2311.152 m) - Math.random更快
  • Node.JS(v0.10.29) - Math.abs更快

思考

通过V8源代码,我希望Math.abs更快,它在Node.JS中,但不在Chrome中。

想法为什么?

暂无
暂无

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

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