这是否意味着Java Math.floor非常慢?

我不是很多Java。

我正在编写一些优化的数学代码,我对我的分析器结果感到震惊。 我的代码收集值,交错数据,然后根据它选择值。 Java运行速度比我的C ++和MATLAB实现慢。

我正在使用javac 1.7.0_05我正在使用Sun / Oracle JDK 1.7.05

存在一个在代码中执行相关任务的floor函数。 java math.floor个人资料结果

  1. 有人知道解决这个问题的范式方法吗?
  2. 我注意到我的floor()函数是用StrictMath定义的。 是否有类似Java的-ffast-math ? 我期待必须有一种方法可以将地板function更改为更合理的,而无需编写自己的function。

     public static double floor(double a) { return StrictMath.floor(a); // default impl. delegates to StrictMath } 

编辑

所以有些人建议我尝试演员。 我尝试了这一点,并且壁挂时间绝对没有变化。

 private static int flur(float dF) { return (int) dF; } 

413742铸造地板function

394675 Math.floor

这些测试没有分析器。 我们努力使用分析器,但运行时间发生了翻天覆地的变化(15分钟以上,所以我退出了)。

这是一个完整性检查您的假设,即代码实际上花了99%的时间在floor 。 假设您拥有算法的Java和C ++版本,这些版本在它们产生的输出方面都是正确的。 为了论证,让我们假设两个版本相同的floor函数调用相同的次数。 所以时间函数是

 t(input) = nosFloorCalls(input) * floorTime + otherTime(input) 

其中floorTime是在平台上调用floorfloorTime的时间。

现在,如果你的假设是正确的,并且在Java上使用floorTime成本要高得多(在大约99%的执行时间内),那么你会期望Java版本的应用程序运行一个很大的因素(50倍或更多) )比C ++版本慢。 如果你没有看到这一点,那么你的假设很可能是错误的。


如果假设为假,则以下是对分析结果的两种替代解释。

  1. 这是测量exception; 即剖析器以某种方式弄错了。 尝试使用不同的探查器。

  2. 您的代码的Java版本中存在一个错误,它导致它比C ++版本的代码调用多次,多次。

您可能想尝试一下FastMath 。

这是一篇关于Java与Javascript中的Math性能的post。 关于默认数学库缓慢的原因,有一些很好的提示。 他们正在讨论除floor以外的其他行动,但我猜他们的调查结果可以推广。 我发现它很有趣。

编辑

根据这个错误条目 ,floor已在7(b79),6u21(b01)中实现了纯java代码,从而提高了性能。 JDK 6中的楼层代码仍然比FastMath中的楼层代码长,但可能不负责这样的性能。 降解。 您使用的JDK是什么? 你可以试试更新的版本吗?

Math.floor()在我的机器上运行速度非常快,每次调用大约7纳秒。 (Windows 7,Eclipse,Oracle JDK 7)。 我希望它在几乎所有情况下都会非常快,如果它成为瓶颈,那将会非常惊讶。

一些想法:

  • 我建议在没有运行探查器的情况下重新运行一些基准测试。 有时候,分析器在检测二进制文件时会产生虚假的开销 – 特别是对于可能内联的Math.floor()这样的小函数。
  • 尝试几个不同的JVM,你可能会遇到一个不起眼的bug
  • 在优秀的Apache Commons Math库中尝试FastMath类,其中包括一个新的floor实现。 如果它更快,我会感到非常惊讶 ,但你永远不会知道。
  • 检查您是否正在运行任何可能干扰Java调用本机代码的能力的虚拟化技术或类似技术(在少数java.lang.Math函数中使用,包括Math.floor()

值得注意的是,监视方法需要一些开销,而在VisualVM的情况下,这是相当高的。 如果你有一个经常被调用的方法,但很少有它可以看起来使用大量的CPU。 例如,我曾经看过Integer.hashCode()作为一名大打击手。 ;)

在我的机器上,地板需要5.6 ns,但是需要2.3 ns。 您可能想在您的计算机上尝试此操作。


除非您需要处理角落情况,否则普通投射会更快。

 // Rounds to zero, instead of Negative infinity. public static double floor(double a) { return (long) a; } 

 public static void main(String... args) { int size = 100000; double[] a = new double[size]; double[] b = new double[size]; double[] c = new double[size]; for (int i = 0; i < a.length; i++) a[i] = Math.random() * 1e6; for (int i = 0; i < 5; i++) { timeCast(a, b); timeFloor(a, c); for (int j = 0; j < size; j++) if (b[i] != c[i]) System.err.println(a[i] + ": " + b[i] + " " + c[i]); } } public static double floor(double a) { return a < 0 ? -(long) -a : (long) a; } private static void timeCast(double[] from, double[] to) { long start = System.nanoTime(); for (int i = 0; i < from.length; i++) to[i] = floor(from[i]); long time = System.nanoTime() - start; System.out.printf("Cast took an average of %.1f ns%n", (double) time / from.length); } private static void timeFloor(double[] from, double[] to) { long start = System.nanoTime(); for (int i = 0; i < from.length; i++) to[i] = Math.floor(from[i]); long time = System.nanoTime() - start; System.out.printf("Math.floor took an average of %.1f ns%n", (double) time / from.length); } 

版画

 Cast took an average of 62.1 ns Math.floor took an average of 123.6 ns Cast took an average of 61.9 ns Math.floor took an average of 6.3 ns Cast took an average of 47.2 ns Math.floor took an average of 6.5 ns Cast took an average of 2.3 ns Math.floor took an average of 5.6 ns Cast took an average of 2.3 ns Math.floor took an average of 5.6 ns 

首先:你的探查器显示你花费了99%的cpu时间在楼层function。 这并不表示地板很慢。 如果你什么都不做,只有地板()完全理智。 然而,由于其他语言似乎更有效地实现楼层,因此您的假设可能是正确的。

我从学校知道,一个天真的地板实现(只适用于正数,一个是负数)可以通过转换为整数/长来完成。 这与语言无关,也是CS课程的一些常识。

这是一些微型长凳。 在我的机器上工作并支持我在学校学到的东西;)

 rataman@RWW009 ~/Desktop $ javac Cast.java && java Cast 10000000 Rounds of Casts took 16 ms rataman@RWW009 ~/Desktop $ javac Floor.java && java Floor 10000000 Rounds of Floor took 140 ms 

 public class Cast/Floor { private static final int ROUNDS = 10000000; public static void main(String[] args) { double[] vals = new double[ROUNDS]; double[] res = new double[ROUNDS]; // awesome testdata for(int i = 0; i < ROUNDS; i++) { vals[i] = Math.random() * 10.0; } // warmup for(int i = 0; i < ROUNDS; i++) { res[i] = floor(vals[i]); } long start = System.currentTimeMillis(); for(int i = 0; i < ROUNDS; i++) { res[i] = floor(vals[i]); } System.out.println(ROUNDS + " Rounds of Casts took " + (System.currentTimeMillis() - start) +" ms"); } private static double floor(double arg) { // Floor.java return Math.floor(arg); // or Cast.java return (int)arg; } 

}