是& 检查奇数时比 % 更快?

发布于 2024-07-26 10:51:11 字数 189 浏览 1 评论 0原文

要检查奇数和偶数,最低位检查是否比使用模数更有效?

>>> def isodd(num):
        return num & 1 and True or False

>>> isodd(10)
False
>>> isodd(9)
True

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

>>> def isodd(num):
        return num & 1 and True or False

>>> isodd(10)
False
>>> isodd(9)
True

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

仅此而已 2024-08-02 10:51:12

是的。 标准库中的 timeit 模块是您检查这些内容的方式。 例如:

$ python -m timeit -s 'def isodd(x): x & 1' 'isodd(9)'
1000000 loops, best of 3: 0.446 usec per loop
$ python -m timeit -s 'def isodd(x): x & 1' 'isodd(10)'
1000000 loops, best of 3: 0.443 usec per loop
$ python -m timeit -s 'def isodd(x): x % 2' 'isodd(9)'
1000000 loops, best of 3: 0.461 usec per loop
$ python -m timeit -s 'def isodd(x): x % 2' 'isodd(10)'
1000000 loops, best of 3: 0.453 usec per loop

如您所见,在我的 (first-day==old==slow;-) Macbook Air 上,& 解决方案可重复地比 %< 快 7 到 18 纳秒/代码> 解决方案。

timeit 不仅告诉您哪个更快,而且还告诉您快了多少(只需运行测试几次),这通常表明它是多么不重要(您真的关心吗? 10 纳秒的差异,当调用函数的开销约为 400 时?!-)...

让程序员相信微优化本质上是无关紧要的,这已被证明是一项不可能完成的任务——尽管已经过去了 35 年(计算机在这段时间里)速度提高了几个数量级!)自从 Knuth 写道

我们应该忘记小事
效率大约是 97%
时间:过早优化是
万恶之源。

正如他所解释的那样,这句话引用自霍尔的一份更古老的声明。 我想每个人都完全相信他们的情况属于剩下的 3%!

因此,我们(特别是 Tim Peters 值得尊敬)没有无休止地重复“没关系”,而是放入了标准 Python 库模块 timeit,这使得测量此类微量数据变得非常容易。基准,从而至少让一些程序员说服自己,嗯,这个案例确实属于 97% 组!-)

Yep. The timeit module in the standard library is how you check on those things. E.g:

$ python -m timeit -s 'def isodd(x): x & 1' 'isodd(9)'
1000000 loops, best of 3: 0.446 usec per loop
$ python -m timeit -s 'def isodd(x): x & 1' 'isodd(10)'
1000000 loops, best of 3: 0.443 usec per loop
$ python -m timeit -s 'def isodd(x): x % 2' 'isodd(9)'
1000000 loops, best of 3: 0.461 usec per loop
$ python -m timeit -s 'def isodd(x): x % 2' 'isodd(10)'
1000000 loops, best of 3: 0.453 usec per loop

As you see, on my (first-day==old==slow;-) Macbook Air, the & solution is repeatably between 7 and 18 nanoseconds faster than the % solution.

timeit not only tells you what's faster, but by how much (just run the tests a few times), which usually shows how supremely UNimportant it is (do you really care about 10 nanoseconds' difference, when the overhead of calling the function is around 400?!-)...

Convincing programmers that micro-optimizations are essentially irrelevant has proven to be an impossible task -- even though it's been 35 years (over which computers have gotten orders of magnitude faster!) since Knuth wrote

We should forget about small
efficiencies, say about 97% of the
time: premature optimization is the
root of all evil.

which as he explained is a quote from an even older statement from Hoare. I guess everybody's totally convinced that THEIR case falls in the remaining 3%!

So instead of endlessly repeating "it doesn't matter", we (Tim Peters in particular deserves the honors there) put in the standard Python library module timeit, that makes it trivially easy to measure such micro-benchmarks and thereby lets at least some programmers convince themselves that, hmmm, this case DOES fall in the 97% group!-)

梦里°也失望 2024-08-02 10:51:12

老实说,我认为这并不重要。

第一个问题是可读性。 什么对其他开发人员更有意义? 我个人在检查数字的偶数/奇数时会期望取模。 我预计大多数其他开发人员也会有同样的期望。 通过引入不同的、意想不到的方法,您可能会使代码阅读和维护变得更加困难。

第二个事实是,在执行任一操作时您可能永远不会遇到瓶颈。 我支持优化,但早期优化是在任何语言或环境中都可以做的最糟糕的事情。 如果由于某种原因,确定一个数字是偶数还是奇数成为瓶颈,那么找到解决问题的最快方法。 然而,这让我回到了我的第一点——第一次编写例程时,应该以最易读的方式编写它。

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.

硬不硬你别怂 2024-08-02 10:51:12

您可以获得的最佳优化是将测试放入函数中。 'number % 2' 和 'number & 1' 是检查奇数/偶数的非常常见的方法,经验丰富的程序员会立即识别该模式,并且如果您确实需要它很明显,您可以随时添加注释,例如“# if number is odd, then blah blah blah” 。

# state whether number is odd or even
if number & 1:
    print "Your number is odd"
else:
    print "Your number is even"

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"
疧_╮線 2024-08-02 10:51:12

“返回 num & 1 和 True 或 False”? 哇! 如果您是速度狂 (1)“return num & 1”(2) 内联它:if somenumber % 2 == 1 清晰且击败 isodd(somenumber) code> 因为它避免了 Python 函数调用。

"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.

羁〃客ぐ 2024-08-02 10:51:12

约翰提出了一个很好的观点。 真正的开销在于函数调用:

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

有趣的是,这两种方法在没有函数调用的情况下同时执行。

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.

生死何惧 2024-08-02 10:51:12

使用Python 3.6,答案是。 在 2017 MBP 上使用下面的代码表明使用模更快。

# 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)

给出这个结果:

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

正如其他答案中所建议的,函数调用是一个很大的开销,但是,删除它表明模数仍然比按位更快,并且在 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()

结果:

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

奖励:事实证明这需要大约两倍的时间在 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

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
生生漫 2024-08-02 10:51:12

除了邪恶的优化之外,它还消除了每个程序员无需多看就能理解的非常惯用的“var % 2 == 0”。 所以这也违反了蟒蛇的禅宗,而且收获甚微。

此外,为了提高可读性,a = b 和 True 或 False 已被替换为

return True if num & 1 其他错误

Apart from the evil optimization, it takes away the very idiomatic "var % 2 == 0" that every coder understands without looking twice. So this is violates pythons zen as well for very little gain.

Furthermore a = b and True or False has been superseded for better readability by

return True if num & 1 else False

思慕 2024-08-02 10:51:12

真的很惊讶上面的答案都没有同时进行变量设置(时间文字是不同的故事)和没有函数调用(这显然隐藏了“较低项”)。 卡在 ipython 的 timeit 的时间上,我得到了明显的获胜者 x&1 - 使用 python2.6 的结果更好,约 18%(使用 python3.1 的约 12%)。

在我很旧的机器上:

$ 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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文