【问题标题】:Why is Javascript's Math.floor the slowest way to calculate floor in Javascript?为什么 Javascript 的 Math.floor 是用 Javascript 计算下限的最慢方法?
【发布时间】:2011-02-01 08:54:46
【问题描述】:

我通常不喜欢微基准测试。但是这个有一个非常有趣的结果。
http://ernestdelgado.com/archive/benchmark-on-the-floor/

这表明Math.floor 是在 Javascript 中计算下限的最慢方法。 ~~nn|nn&n 都更快了。
这似乎很令人震惊,因为我希望在当今的现代浏览器中实现 Javascript 的人会是一些非常聪明的人。

floor 是否做了其他方法无法做到的重要事情?有什么理由使用它吗?

【问题讨论】:

  • ~~nn|nn&n 不会产生与 Math.floor 相同的结果。前三个只能返回 32 位整数。试试n = 50000000000.4
  • 在我的浏览器(FF 3.6.something)中,它们在结果中的速度都是相同的(大约为“3”,无论这意味着什么)。在不同的浏览器上尝试(底部提供了小测试),而不是仅仅从表面上获取结果。确实没有理由让它明显变慢。
  • 实际上,在 FF 3.6 中运行他的基准测试表明 Math.floor 是最快的
  • 它们的行为与 Math.floor 不同 - 它们与 Math.trunc 相同,这对于负非整数是不同的。

标签: javascript optimization


【解决方案1】:

Math.floor 速度较慢的主要原因是它涉及函数调用(实际上在某些测试中我已经完成了它的速度更快)。较旧的 JavaScript 实现无法内联函数调用。较新的引擎可以内联调用,或者至少使属性查找更快,但它们仍然需要保护条件,以防您(或其他脚本)覆盖 Math.floor 函数。虽然开销很小,所以速度上没有太大差异。

更重要的是,正如在几个 cmets 中提到的,其他方法并不等效。它们都通过按位运算来工作。位运算符通过截断数字自动将其操作数转换为 32 位整数。如果数字适合 32 位,那很好,但 JavaScript 数字是 64 位浮点数,可能比 2147483647 大得多。

对于负数,它们也会给出不同的结果,因为转换为整数会截断并且Math.floor 总是向下舍入。例如Math.floor(-2.1) === -3,但(-2.1) | (-2.1) === -2

如果您知道您只处理小于 2147483648 的正数,并且您需要从旧浏览器中的代码中榨取所有性能(首先确保它实际上是瓶颈。它可能不是。),我会使用更简单的方法:x|0。它不会对变量进行两次计算,即使 x 是一个表达式,它也可以工作(请务必将其放在括号中,以免遇到优先级问题)。

【讨论】:

  • "JavaScript 不能内联函数,因为它是动态的,你可以用不同的函数替换 Math.floor。"那是不合逻辑的。 JIT 编译器可以并且执行内联动态函数调用。跟踪 JIT 尤其擅长这一点。动态性通过警卫检查或添加失效触发器来处理。一个特定的实现可能会或可能不会内联。因此,与所有微优化一样,如果确实很重要,请在您的目标平台上进行衡量。
  • @Ants:好点子。这就是我对缓存函数值的理解,但我措辞不正确。
  • 我知道这个答案已经有几年的历史了,但是 V8 现在内联函数调用,这导致 Math.floor performing as well as bitwise hacks
  • 太棒了@josh3736!感谢您的链接。 +1
【解决方案2】:

它与现代浏览器无关。它与实施 ECMA 标准有关。即使有更快的方法,您也不能只更改某个函数的执行方式。它可能会破坏现有代码。

Math.Floor 必须考虑处理不同类型的许多不同场景。他们是否可以通过您所描述的走捷径来更快地制作不同的场景?也许他们可以,但这可能会破坏其他场景。表面看起来很小,并不代表下面没有冰山。

【讨论】:

  • +1 慢与否,这可能是唯一正确
  • 好点。 Math.floor("foo") 返回NaN。但如果这是唯一的好处,我很想使用另一种方法。至少在 Javascript 支持整数除法之前。
  • 我并不是说你应该总是使用 Math.Floor,它很慢。只是当他们设计一个功能时,他们必须考虑人们可能会做的很多不同的场景。关于 JS 的一件事是它很容易编程,这意味着他们在构建它时必须采取额外的预防措施,并且在某些情况下需要很长的路要走。
  • @z5h - 另一个好处是 Math.floor() 正确地向下舍入负数,而大多数位运算符向 0 舍入(尽管 >>> 完全超出图表)。
猜你喜欢
  • 2010-09-13
  • 1970-01-01
  • 2010-10-29
  • 1970-01-01
  • 2010-12-15
  • 2017-06-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多