在Java中,按位运算比模数/余数运算符更快吗?

时间:2014-02-19 19:00:41

标签: java bit-manipulation

我在几篇博客中读到,在Java模数/提醒操作符中比按位-EN慢。所以,我编写了以下程序进行测试。

public class ModuloTest {
    public static void main(String[] args) {
        final int size = 1024;
        int index = 0;

        long start = System.nanoTime();
        for(int i = 0; i < Integer.MAX_VALUE; i++) {
            getNextIndex(size, i);
        }
        long end = System.nanoTime();
        System.out.println("Time taken by Modulo (%) operator --> " + (end - start) + "ns.");

        start = System.nanoTime();
        final int shiftFactor = size - 1;
        for(int i = 0; i < Integer.MAX_VALUE; i++) {
            getNextIndexBitwise(shiftFactor, i);
        }
        end = System.nanoTime();
        System.out.println("Time taken by bitwise AND --> " + (end - start) + "ns.");
    }

    private static int getNextIndex(int size, int nextInt) {
        return nextInt % size;
    }

    private static int getNextIndexBitwise(int size, int nextInt) {
        return nextInt & size;
    }
}

但在我的运行时环境(MacBook Pro 2.9GHz i7,8GB RAM,JDK 1.7.0_51)中,我看到了其他情况。按位-AND明显更慢,实际上比余数运算符慢两倍。

如果有人可以帮助我了解这是出于预期的行为还是我做错了什么,我将不胜感激?

谢谢, NIRANJAN

5 个答案:

答案 0 :(得分:4)

你的代码按比例报告 - 并且在我尝试过的每台Mac上都快得多,包括Java 6和Java 7.我怀疑你的机器上的第一部分测试恰好与系统上的其他活动一致。您应该多次尝试运行测试,以验证您没有看到基于此的扭曲。 (我会把它留作'评论'而不是'答案',但显然你需要50点声望 - 如果你问我,那就太傻了。)

答案 1 :(得分:1)

这个例子特别会给你一个错误的结果。此外,我相信任何用2的幂计算模数的程序都会比按位AND快。

原因:当你使用N%X,其中X是k的2次幂时,只有最后k位被认为是模数,而在按位AND运算符的情况下,运行时实际上必须访问每个有问题的数字。

另外,我想指出热点JVM优化类似性质的重复计算(其中一个例子可以是分支预测等)。在你的情况下,使用模数的方法只返回数字的最后10位,因为1024是2的10次幂。

尝试使用一些素数值作为大小并检查相同的结果。

免责声明:微基准测试不算好。

答案 2 :(得分:1)

这种方法遗漏了什么吗?

public static void oddVSmod(){
        float tests = 100000000;
        oddbit(tests);
        modbit(tests);
    }
    public static void oddbit(float tests){
        for(int i=0; i<tests; i++)
            if((i&1)==1) {System.out.print(" "+i);}
        System.out.println();
    }
    public static void modbit(float tests){
        for(int i=0; i<tests; i++)
            if((i%2)==1) {System.out.print(" "+i);}
        System.out.println();
    }

有了这个,我使用netbeans内置的分析器(高级模式)来运行它。我将var测试设置为10X10 ^ 8,并且每次都显示模数比按位快。

答案 3 :(得分:1)

对于初学者来说,逻辑连接技巧仅适用于自然数红利和2个除数的幂。因此,如果您需要负红利,浮动或2的非权力,请使用默认的%运营商。

My tests(使用JIT预热和1M随机迭代),在具有大量核心和总线负载的ram的i7上显示<按>按比例增加<20>更好的性能。这可以非常运行,具体取决于进程调度程序如何运行代码。

  • 在JDK 1.8.91上使用Scala 2.11.8
  • 4Ghz i7-4790K,8核AMD,32GB PC3 19200 ram,SSD

答案 4 :(得分:0)

谢谢大家的宝贵意见。

@pamphlet:非常感谢您的关注,但负面评论对我很好。我承认我没有按照AndyG的建议进行适当的测试。 AndyG本来可以使用更柔和的音调,但是没关系,有时负片有助于看到积极因素。 :)

也就是说,我改变了我的代码(如下所示),我可以多次运行该测试。

public class ModuloTest {
    public static final int SIZE = 1024;

    public int usingModuloOperator(final int operand1, final int operand2) {
        return operand1 % operand2;
    }

    public int usingBitwiseAnd(final int operand1, final int operand2) {
        return operand1 & operand2;
    }

    public void doCalculationUsingModulo(final int size) {
        for(int i = 0; i < Integer.MAX_VALUE; i++) {
            usingModuloOperator(1, size);
        }
    }

    public void doCalculationUsingBitwise(final int size) {
        for(int i = 0; i < Integer.MAX_VALUE; i++) {
            usingBitwiseAnd(i, size);
        }
    }

    public static void main(String[] args) {
        final ModuloTest moduloTest = new ModuloTest();

        final int invocationCount = 100;
//        testModuloOperator(moduloTest, invocationCount);

        testBitwiseOperator(moduloTest, invocationCount);
    }

    private static void testModuloOperator(final ModuloTest moduloTest, final int invocationCount) {
        for(int i = 0; i < invocationCount; i++) {
            final long startTime = System.nanoTime();
            moduloTest.doCalculationUsingModulo(SIZE);
            final long timeTaken = System.nanoTime() - startTime;
            System.out.println("Using modulo operator // Time taken for invocation counter " + i + " is " + timeTaken + "ns");
        }
    }

    private static void testBitwiseOperator(final ModuloTest moduloTest, final int invocationCount) {
        for(int i = 0; i < invocationCount; i++) {
            final long startTime = System.nanoTime();
            moduloTest.doCalculationUsingBitwise(SIZE);
            final long timeTaken = System.nanoTime() - startTime;
            System.out.println("Using bitwise operator // Time taken for invocation counter " + i + " is " + timeTaken + "ns");
        }
    }
}

我以互相排斥的方式致电testModuloOperator()testBitwiseOperator()。结果与按位比模运算符更快的想法一致。我运行了100次计算并记录了执行时间。然后删除前五个和最后五个录音,并使用休息来计算平均值。时间。以下是我的测试结果。

  1. 使用模运算符,平均值。 90次运行的时间:8388.89ns
  2. 使用按位运算符,平均值。 90次运行的时间:722.22ns
  3. 请说明我的方法是否正确。

    再次感谢。 NIRANJAN