Is bitwise operation faster than modulo/reminder operator in Java?

独自空忆成欢 提交于 2019-12-10 15:45:06

问题


I read in couple of blogs that in Java modulo/reminder operator is slower than bitwise-AND. So, I wrote the following program to test.

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;
    }
}

But in my runtime environment (MacBook Pro 2.9GHz i7, 8GB RAM, JDK 1.7.0_51) I am seeing otherwise. The bitwise-AND is significantly slower, in fact twice as slow than the remainder operator.

I would appreciate it if someone can help me understand if this is intended behavior or I am doing something wrong?

Thanks, Niranjan


回答1:


Your code reports bitwise-and being much faster on each Mac I've tried it on, both with Java 6 and Java 7. I suspect the first portion of the test on your machine happened to coincide with other activity on the system. You should try running the test multiple times to verify you aren't seeing distortions based on that. (I would have left this as a 'comment' rather than an 'answer', but apparently you need 50 reputation to do that -- quite silly, if you ask me.)




回答2:


This example in particular will always give you a wrong result. Moreover, I believe that any program which is calculating the modulo by a power of 2 will be faster than bitwise AND.

REASON: When you use N % X where X is kth power of 2, only last k bits are considered for modulo, whereas in case of the bitwise AND operator the runtime actually has to visit each bit of the number under question.

Also, I would like to point out the Hot Spot JVM's optimizes repetitive calculations of similar nature(one of the examples can be branch prediction etc). In your case, the method which is using the modulo just returns the last 10 bits of the number because 1024 is the 10th power of 2.

Try using some prime number value for size and check the same result.

Disclaimer: Micro benchmarking is not considered good.




回答3:


Is this method missing something?

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();
    }

With that, i used netbeans built-in profiler (advanced-mode) to run this. I set var tests up to 10X10^8, and every time, it showed that modulo is faster than bitwise.




回答4:


For starters, logical conjunction trick only works with Nature Number dividends and power of 2 divisors. So, if you need negative dividends, floats, or non-powers of 2, sick with the default % operator.

My tests (with JIT warmup and 1M random iterations), on an i7 with a ton of cores and bus load of ram show about 20% better performance from the bitwise operation. This can very per run, depending how the process scheduler runs the code.

  • using Scala 2.11.8 on JDK 1.8.91
  • 4Ghz i7-4790K, 8 core AMD, 32GB PC3 19200 ram, SSD



回答5:


Thank you all for valuable inputs.

@pamphlet: Thank you very much for the concerns, but negative comments are fine with me. I confess that I did not do proper testing as suggested by AndyG. AndyG could have used a softer tone, but its okay, sometimes negatives help seeing the positive. :)

That said, I changed my code (as shown below) in a way that I can run that test multiple times.

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");
        }
    }
}

I called testModuloOperator() and testBitwiseOperator() in mutual exclusive way. The result was consistent with the idea that bitwise is faster than modulo operator. I ran each of the calculation 100 times and recorded the execution times. Then removed first five and last five recordings and used rest to calculate the avg. time. And, below are my test results.

  1. Using modulo operator, the avg. time for 90 runs: 8388.89ns.
  2. Using bitwise-AND operator, the avg. time for 90 runs: 722.22ns.

Please suggest if my approach is correct or not.

Thanks again. Niranjan



来源:https://stackoverflow.com/questions/21889838/is-bitwise-operation-faster-than-modulo-reminder-operator-in-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!