【问题标题】:Fast integer division in JavaJava中的快速整数除法
【发布时间】:2017-08-08 00:30:53
【问题描述】:

众所周知,整数除法运算速度很慢(通常比整数乘法慢几倍)。但是,如果需要使用固定除数执行许多除法运算,则可以对除数进行一些预处理并将“/”替换为乘法和位运算(Hacker's Delight 中的第 10 章)。

正如我所测试的,如果除数是编译时常量(例如static final long DIVISOR = 12345L;),JVM 将使用乘法和位运算将所有除数替换为 DIVISOR。我对同样的技巧很感兴趣,但除数仅在运行时才知道。

例如下面的(慢)方法:

void reduceArraySlow(long[] data, long denominator){
    for(int i = 0; i < data.length; ++i)
        data[i] = data[i] / denominator;
}

可以替换为:

void reduceArrayFast(long[] data, long denominator){
    SomeMagicStructure magic = computeMagic(denominator);
    for(int i = 0; i < data.length; ++i)
         // computes data[i] / denominator
        data[i] = doFastDivision(data[i], magic);
}

它必须更快地完成这项工作,因为所有 / 操作都被更快的操作所取代(并且还因为除法不是在 CPU 中流水线化的)。

【问题讨论】:

    标签: java performance division integer-division


    【解决方案1】:

    有众所周知的 C/C++ libdivide 库用于快速整数除法,还有我为 Java 改编的这个库 libdivide4j

    使用 libdivide4j 进行快速除法如下:

    void reduceArrayFast(long[] data, long denominator){
        FastDivision.Magic magic = FastDivision.magicSigned(denominator);
        for(int i = 0; i < data.length; ++i)
            // computes data[i] / denominator
            data[i] = FastDivision.divideSignedFast(data[i], magic);
    }
    

    一个简单的基准测试

    public void benchmark() throws Exception {
        Random rnd = new Random();
        int nIterations = 10000;
        //let the JIT to optimize something
        for (int att = 0; att < nIterations; att++) {
            long[] data = new long[1000];
            for (int i = 0; i < data.length; i++)
                data[i] = rnd.nextLong();
    
            long denominator = rnd.nextLong();
    
            long[] slow = data.clone();
            long start = System.nanoTime();
            reduceArraySlow(slow, denominator);
            long slowTime = System.nanoTime() - start;
    
    
            long[] fast = data.clone();
            start = System.nanoTime();
            reduceArrayFast(fast, denominator);
            long fastTime = System.nanoTime() - start;
    
            Assert.assertArrayEquals(slow, fast);
    
            // print last 100 timings (JVM already warmed up)
            if (att > nIterations - 100) {
                System.out.println("\"/\" operation: " + slowTime);
                System.out.println("Fast division: " + fastTime);
                System.out.println("");
            }
        }
    }
    

    显示了普通/和快速除法(Core i7,jdk8 64位)的以下时序(纳秒):

    "/" operation: 13233
    Fast division: 5957
    
    "/" operation: 13148
    Fast division: 5103
    
    "/" operation: 13587
    Fast division: 6188
    
    "/" operation: 14173
    Fast division: 6773
    ...
    

    【讨论】:

      猜你喜欢
      • 2022-01-05
      • 2016-06-10
      • 1970-01-01
      • 1970-01-01
      • 2018-02-26
      • 2016-07-05
      • 2023-01-18
      • 1970-01-01
      • 2012-06-27
      相关资源
      最近更新 更多