【问题标题】:int vs float arithmetic efficiency in JavaJava中的int vs float算术效率
【发布时间】:2011-03-22 00:25:29
【问题描述】:

我正在编写一个应用程序,它使用 Dijkstra 算法在图中找到最小路径。图中节点和边的权重是float 数字,因此该算法对浮点数进行了许多算术运算。如果我将所有重量转换为ints,我能否提高运行时间? Java 中的 int 算术运算比浮点运算快吗?

我尝试编写一个简单的基准来检查这一点,但我对得到的结果并不满意。可能编译器已经优化了程序的某些部分,所以结果对我来说看起来不太好。


编辑:

我要解决的问题是信息检索字段。应用程序应显示作为一组关键字的查询的答案。

我的数据结构是加权有向图。给定一组叶节点,我必须找到连接这些节点的最小树并向用户显示答案。权重由部分基于 tf/idf 技术的加权函数分配。用户不知道我为节点和边分配了什么权重,他只想查看与他提出的查询相关的答案。所以不需要精确的结果,只是可以根据他们的权重列举答案。只是权重函数的原生使用(正如我提到的,它基于 tf/idf)给出了浮点权重,所以到目前为止我一直使用浮点数。

我希望这可以为这个问题增加一些背景。

【问题讨论】:

  • 我知道乘以整数会快一点,大约 13%,但比较两个整数会慢 22%。
  • 我不完全确定,但对于 Dijkstra 来说,只需加法和比较操作就足够了。对于这些操作,float 或 int 的变化不应太大。我真的很惊讶整数比较会慢 22%。我可以了解一下您进行了哪种基准测试?
  • 我正在制作一个随机整数数组和一个随机浮点数组,然后测量对这些数字进行比较和乘法运算所需的时间。也许只是迭代数组需要很长时间,所以我想知道是否有一些关于这个主题的文献。

标签: java performance math primitive


【解决方案1】:

对于简单的操作 int 更快,但是使用 int 您可能需要做更多的工作才能获得相同的结果。例如

浮动

float f = 15 * 0.987;

作为整数

int i = 15 * 987 / 1000;

额外的除法意味着 int 操作可能需要更长的时间。

【讨论】:

  • 在 Dijkstra 算法中,我只是总结和比较了路径的权重,所以划分操作对我来说非常落后。你怎么知道对于简单的操作整数更快,这是一个常见的场景,或者你可以给我指出一些关于这个主题的文献。
  • 你需要查看JVM生成的本机代码并比较时钟周期。但是,与缓存未命中和系统调用的成本相比,这两种操作都相当快。数据类型的选择很可能不会有太大的不同。
【解决方案2】:

与以往一样,您应该为自己设定一些性能目标,然后分析应用程序以查看它是否符合这些目标。

您经常会发现令人惊讶的结果;所花费的时间几乎不受基本数值类型的影响,或者您的算法不是最佳的。

关于编译器优化 - 它们是性能优化中真实有效的部分。

如果理论上使用类型 A 比使用类型 B 更快,但您的编译器可以优化类型 B 以在实际场景中更快,那么这是一个有价值的证据,而不是令人失望的根源。

【讨论】:

  • 我只是想知道性能改进是否值得我花时间更改应用程序的相当好的部分。但似乎我无法提前确定这一点,最好的检查方法是实现两个版本的算法并测量运行时间。
【解决方案3】:

我不这么认为。

浮点数为 4 个字节。 Java中的Int也是4字节。

为什么不使用 Date(java.util.Date) 来获取运行时间?

您可以定义一个拥有 100000 个节点的图。然后计算一下。

【讨论】:

  • 您可能想要使用单词“byte”而不是“bit”。一个 4 bit 整数只能容纳 16 个不同的值……
  • 对不起...我的英文很差。所以我用错了词。(我的母语不是英文)实际上,我认为如果速度比float快,那是因为硬件。在物理学中,int可能比float容易实现。
【解决方案4】:

如果你只是想比较权重,你应该更喜欢int而不是float。

【讨论】:

  • 与另一个答案的评论相同:这是一个常见的场景,或者您可以向我指出有关该主题的一些文献。
【解决方案5】:

一般而言,出于性能原因,您不必担心在 intfloat 之间进行选择。

这是Java Puzzlers附录的摘录:

浮点运算不精确。不要在需要精确结果的地方使用浮点数;相反,请使用整数类型或BigDecimal。更喜欢double 而不是float

除非你有很好的理由,否则如果你必须使用浮点运算,你通常应该更喜欢double 而不是float。如果需要确切的结果,请继续使用BigDecimal;它会比较慢,因为它不是原始的,但除非分析表明它是不可接受的,否则这通常是最好的选择。

如果您必须使用浮点运算,那么尝试通过使用int 来优化它是不明智的。这可能是一个过早的优化,只会使不必要的代码复杂化。用最自然、最易读的方式写出来。不要为了轻微的性能提升而使您的代码不必要地复杂化。

如果您实际上不需要浮点运算,那么请务必使用intlong

【讨论】:

【解决方案6】:

我认为性能很大程度上取决于算法和软件运行的平台。

如果您在 X86 平台上进行矩阵/数组计算,运行时可能会对其进行优化以使用 SSE,这是一个仅限浮点/双精度的扩展指令集。

在其他平台上,运行时可能会针对 OpenCL 进行优化(我不相信现在有人这样做,但它可能会发生:)。我不知道在这样的平台上什么运行速度最快,以及在什么条件下运行。可能只是 OpenCL 针对整数工作负载进行了优化。

在这种情况下,我会得出结论,此时优化数据类型(float 或 int)是没有用的,而只是优化代码的可读性。

如果您的代码对性能至关重要,并且您确切地知道系统现在和将来将在哪个硬件上运行,您可以使用各种算法测试典型工作负载并选择最能满足您需求的一种。

但总的来说,只需使用您可以理解的算法,保持代码可读性,从而降低错误数量。如果结果不正确,快速代码就不值那么多钱了 :)

【讨论】:

    【解决方案7】:

    在我的机器上,整数减法比双减法快约 2.5 倍。 然而,整数乘法仅比双乘法快约 1.5 倍。

    以下测试适用于随机数据,这可能会阻止编译器进行优化。

    // test whether int subs are faster than double subs
    public void compareIntAndFloatSubtraction(){
    
        int N = 100000;  // input array size
        int k = 100000;  // number of mathematical operations performed on each element
    
        // generate random data
        int[] ints = new int[N];
        double[] doubles = new double[N];
        Random r = new Random(1l);
        for (int i = 0; i < N; i++) {
            ints[i] = r.nextInt();
            doubles[i] = r.nextDouble();
        }
    
        // measure integer subtractions
        long before = System.currentTimeMillis();
        for (int i = 1; i < N; i++) {
            for (int j = 0; j < k; j++) {
                ints[i] -= ints[i-1];  // referring to another element might prevent from optimization also
            }
        }
        System.out.println(String.format("time needed for int subs [ms]: %s", System.currentTimeMillis()-before));
    
        // measure double subtractions
        before = System.currentTimeMillis();
        for (int i = 1; i < N; i++) {
            for (int j = 0; j < k; j++) {
                doubles[i] -= doubles[i-1];
            }
        }
        System.out.println(String.format("time needed for double subs [ms]: %s", System.currentTimeMillis()-before));
    
    }
    

    【讨论】:

    • 感谢您的测试。在我的机器(Xeon,64 位,Windows,Java 1.8)上运行它进行减法测试得到:int subs [ms] 所需的时间:7704,double subs [ms] 所需的时间:10869。我更感兴趣的是更大小于和小于操作,因为我正在测试它以用于构建 TreeMap。比较操作的结果 /* if (doubles[i] 双打[i-1]) tmp++; */ 是:int cmp [ms] 所需的时间:8479,double cmp [ms] 所需的时间:15925。
    • 仅供参考,我再次对“长”数据类型进行了测试。减法结果:long subs所需时间[ms]:7513,double subs所需时间[ms]:10898。比较结果:long cmp所需时间[ms]:15768,double cmp所需时间[ ms]:16012。对于“longs”,似乎性能与相等性检查相似。
    • 你不能在同一次执行中对不同的值类型进行基准测试,JVM会很热
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-25
    • 1970-01-01
    • 2020-10-22
    • 2012-12-17
    • 2014-08-09
    • 1970-01-01
    • 2012-05-27
    相关资源
    最近更新 更多