Is & faster than % when checking for odd numbers?

前端 未结 8 935
鱼传尺愫
鱼传尺愫 2020-11-30 04:13

To check for odd and even integer, is the lowest bit checking more efficient than using the modulo?

>>> def isodd(num):
        return num & 1 a         


        
相关标签:
8条回答
  • 2020-11-30 04:19

    "return num & 1 and True or False" ? Wah! If you're speed-crazy (1) "return num & 1" (2) inline it: if somenumber % 2 == 1 is legible AND beats isodd(somenumber) because it avoids the Python function call.

    0 讨论(0)
  • 2020-11-30 04:24

    Was really surprised none of the above answers did both variable setup (timing literal is different story) and no function invocation (which obviously hides "lower terms"). Stuck on that timing from ipython's timeit, where I got clear winner x&1 - better for ~18% using python2.6 (~12% using python3.1).

    On my very old machine:

    $ python -mtimeit -s 'x = 777' 'x&1'
    10000000 loops, best of 3: 0.18 usec per loop
    $ python -mtimeit -s 'x = 777' 'x%2'
    1000000 loops, best of 3: 0.219 usec per loop
    
    $ python3 -mtimeit -s 'x = 777' 'x&1'
    1000000 loops, best of 3: 0.282 usec per loop
    $ python3 -mtimeit -s 'x = 777' 'x%2'
    1000000 loops, best of 3: 0.323 usec per loop
    
    0 讨论(0)
  • 2020-11-30 04:31

    The best optimization you can get is to not put the test into a function. 'number % 2' and 'number & 1' are very common ways of checking odd/evenness, experienced programmers will recognize the pattern instantly, and you can always throw in a comment such as '# if number is odd, then blah blah blah' if you really need it to be obvious.

    # state whether number is odd or even
    if number & 1:
        print "Your number is odd"
    else:
        print "Your number is even"
    
    0 讨论(0)
  • 2020-11-30 04:38

    John brings up a good point. The real overhead is in the function call:

    me@localhost ~> python -mtimeit -s'9 % 2'
    10000000 loops, best of 3: 0.0271 usec per loop
    me@localhost ~> python -mtimeit -s'10 % 2'
    10000000 loops, best of 3: 0.0271 usec per loop
    
    me@localhost ~> python -mtimeit -s'9 & 1'
    10000000 loops, best of 3: 0.0271 usec per loop
    me@localhost ~> python -mtimeit -s'9 & 1'
    10000000 loops, best of 3: 0.0271 usec per loop
    
    me@localhost ~> python -mtimeit -s'def isodd(x): x % 2' 'isodd(10)'
    1000000 loops, best of 3: 0.334 usec per loop
    me@localhost ~> python -mtimeit -s'def isodd(x): x % 2' 'isodd(9)'
    1000000 loops, best of 3: 0.358 usec per loop
    
    me@localhost ~> python -mtimeit -s'def isodd(x): x & 1' 'isodd(10)'
    1000000 loops, best of 3: 0.317 usec per loop
    me@localhost ~> python -mtimeit -s'def isodd(x): x & 1' 'isodd(9)'
    1000000 loops, best of 3: 0.319 usec per loop
    

    Interestingly both methods remore the same time without the function call.

    0 讨论(0)
  • 2020-11-30 04:41

    Using Python 3.6 the answer is no. Using the code bellow on a 2017 MBP shows that using modulo is faster.

    # odd.py
    from datetime import datetime
    
    iterations = 100_000_000
    
    
    def is_even_modulo(n):
        return not n % 2
    
    
    def is_even_and(n):
        return not n & 1
    
    
    def time(fn):
        start = datetime.now()
        for i in range(iterations, iterations * 2):
            fn(i)
        print(f'{fn.__name__}:', datetime.now() - start)
    
    
    time(is_even_modulo)
    time(is_even_and)
    

    Gives this result:

    $ python3 -m odd
    is_even_modulo: 0:00:14.347631
    is_even_and: 0:00:17.476522
    $ python3 --version
    Python 3.6.1
    

    As suggested in other answers, the function calls is a large overhead, however, removing it shows that modulo is still faster than bitwise and in Python 3.6.1:

    # odd.py
    from datetime import datetime
    
    iterations = 100_000_000
    
    
    def time_and():
        start = datetime.now()
        for i in range(iterations):
            i & 1 
        print('&:', datetime.now() - start)
    
    
    def time_modulo():
        start = datetime.now()
        for i in range(iterations):
            i % 2
        print('%:', datetime.now() - start)
    
    
    time_modulo()
    time_and()
    

    Results:

    $ python3 -m odd
    %: 0:00:05.134051
    &: 0:00:07.250571
    

    Bonus: it turns out this takes about double the time to run in Python 2.7.

    $ time python2 -m odd
    ('&:', '0:00:20.169402')
    ('%:', '0:00:19.837755')
    
    real    0m41.198s
    user    0m39.091s
    sys 0m1.899s
    $ time python3 -m odd
    &: 0:00:11.375059
    %: 0:00:08.010738
    
    real    0m19.452s
    user    0m19.354s
    sys 0m0.042s
    
    0 讨论(0)
  • 2020-11-30 04:42

    To be totally honest, I don't think it matters.

    The first issue is readability. What makes more sense to other developers? I, personally, would expect a modulo when checking the evenness/oddness of a number. I would expect that most other developers would expect the same thing. By introducing a different, and unexpected, method, you might make code reading, and therefore maintenance, more difficult.

    The second is just a fact that you probably won't ever have a bottleneck when doing either operation. I'm for optimization, but early optimization is the worst thing you can do in any language or environment. If, for some reason, determining if a number is even or odd is a bottleneck, then find the fastest way of solving the problem. However, this brings me back to my first point - the first time you write a routine, it should be written in the most readable way possible.

    0 讨论(0)
提交回复
热议问题