位运算是否仍然实用?

9
维基百科,真正的知识源头,指出:
在大多数旧微处理器上,位运算比加减法运算稍快,通常比乘除法运算快得多。但在现代架构中,情况并非如此:位运算通常与加法速度相同(尽管仍然比乘法快)。
学习位运算技巧是否有实际意义,还是只是为了理论和好奇心呢?

1
维基百科是“真正的知识源”吗?这似乎有点夸张了... - ShinTakezou
12
@ShinTakezou说的是讽刺。 - geekosaur
1
我无法从线索中得知它是讽刺,除非我认为这种说法夸张了。因为我认识一些人认为维基百科真的是一个很好的知识来源,并且总是正确的。而且我不知道OP对此的真实看法和观点,所以我必须认真对待这种说法。很高兴知道这是讽刺。 - ShinTakezou
1
在这种情况下,我们的唯一真正的知识来源甚至对于现代CPU也是完全正确的。请参见Intel的指令表此处。 ADD / SUB和AND / OR / XOR的倒数吞吐量非常相似 ;) - Voo
你怎么敢不尊重维基媒体基金会的名誉![为GNU黎明而战!] (http://xkcd.com/225/) - Mateen Ulhaq
位运算符的实际应用案例 - phuclv
9个回答

17

位运算值得学习,因为它们有许多应用。它们的主要用途不是代替算术运算。密码学、计算机图形学、哈希函数、压缩算法和网络协议只是一些位运算非常有用的示例。

你从维基百科文章中引用的那句话只是试图提供有关位运算速度的一些线索。不幸的是,该文章未能提供一些很好的应用示例。


12

位运算仍然很有用。例如,可以使用它们使用单个变量创建“标志”,并节省指示各种条件所需的变量数量。关于算术操作的性能,最好让编译器进行优化(除非您是某种类型的专家)。


这对于协议或硬件配置等内容是必不可少的,同时也是一个潜在的性能提升来源。它可以大幅度降低代码的内存带宽需求,从而实现比操作周期计数所暗示的更大的性能提升。 - Per Knytt
2
最好让编译器进行优化(除非你是某种大师)。编写编译器或使用糟糕的编译器...学习所有技巧可能并非必要,但学习如何使用位运算符应被视为与正确使用“+”和“,”一样重要。 - James Greenhalgh
1
@James 不太确定。正如此前的一篇帖子所示(可悲的是现在已被删除),很容易出错。而且,位填充大多用于数学密集型操作的性能优化,这些操作应该在某个不错的库中实现。对于空间优化(其中存在一些陷阱),您可以使用隐藏这些内容的位域。因此,基本上只需要编写一些低级协议代码,因为C再次未定义位域的顺序。 - Voo

7

它们有助于理解二进制的“工作原理”,否则没有用处。实际上,我会说,即使按位操作在特定架构上更快,也应该由编译器利用这一点而不是你自己来完成。写出你的意思。


+1 表示“这是编译器的工作”。话虽如此,有时你的意思是将一堆位掩码 | 在一起,然后 & 结果与一个值,以查看各种标志是否已设置。 - Sherm Pendley
@Sherm:当然可以 - 但是这样你就不是“意味着”加法或减法了,因此按位操作在语义上是正确的(而且再次强调,这是编译器的工作来找出最佳方法)。 - geekosaur
1
代码并不总是编译的,这取决于平台和语言,因此如果性能仍然是一个问题,了解和使用位运算可以很方便。 - ricosrealm
我不会把现代解释器优化一些微不足道的位运算视为不可能 - 即使没有。紧密的数学循环很少在解释性语言上运行 - 结果也不会特别好(例如numpy和常规python实现之间的因子50+或其他差异)。 - Voo

2

唯一有意义使用它们的情况是,如果您实际上将数字用作位向量。例如,如果您正在模拟某种硬件,并且变量表示寄存器。

如果您想执行算术运算,请使用算术运算符。


1

当然(对我来说)答案是肯定的:学习它们可能有实际原因。现在,例如,在典型处理器上,add指令的速度与or/xorand相同,这意味着:add在那些处理器上的速度与or相同。

像加法、除法等指令速度的提高只是意味着现在在那些处理器上可以使用它们,并且不必太担心性能影响;但是过去和现在一样,通常不会将每个add更改为位运算符以实现add。也就是说,在某些情况下,这可能取决于哪些技巧:现在可能有些技巧必须被认为是教育性的而不是实用的;其他技巧仍然可能具有实际应用。


这些操作在1979年的旧款Motorola 68000 CPU上已经达到了相同的速度。 - undefined

1

取决于你的问题是什么。如果你正在控制硬件,你需要一些方法来在整数中设置单个位。

购买一个 OGD1 PCI 板(开放图形卡),并使用 libpci 与之通信。http://en.wikipedia.org/wiki/Open_Graphics_Project


1

在大多数情况下,当您将整数乘以恰好是2的幂次方的常数时,编译器会优化为使用位移操作。但是,当移位也是一个变量时,编译器无法推断它,除非您明确使用移位操作。


相反,有些处理器的乘法速度比它们按可变位数移位的速度更快。只需编写可读的内容即可。你的代码中关键的10%可能已经在其他地方了... - Mackie Messer

1
有趣的是,没有人提到在C / C ++中的ctype []数组 - 这个概念在语言处理中非常有用,特别是当使用不同的字母表或解析句子时。ctype []是一个由256个short整数组成的数组,在每个整数中,都有表示不同字符类型的位。例如,ctype [; A'] - ctype ['Z']有设置位以显示它们是字母表中的大写字母; ctype ['0']-ctype ['9']有设置位以显示它们是数字。要查看字符x是否为字母数字,可以编写类似'if (ctype [x]&(UC | LC | NUM))'的代码,这比编写'if('A'= x <= 'Z'|| ....'的代码更快,更优雅。

一旦你开始考虑按位运算,你会发现有很多地方可以使用它。例如,我有两个文本缓冲区。我将一个写到另一个中,边写边用 REPLACEstring 替换所有 FINDstring 的出现。然后对于下一个查找替换对,我只需切换缓冲索引,这样我总是从 buffer [in] 写入到 buffer [out]。'in' 初始为 0,'out' 初始为1。在完成复制后,我只需编写 'in ^= 1; out ^= 1;'。在处理所有替换后,我只需将 buffer [out] 写入磁盘,此时不需要知道 'out' 是什么。

如果您认为这是低级别的,请考虑某些类似于 déjà vu 和其孪生兄弟 jamais vu 的心理错误是由大脑位错误引起的!


0

在处理IPv4地址时,经常需要进行位运算,以确定对等方的地址是否在可路由网络内或必须转发到网关,或者对等方是否属于防火墙规则允许或拒绝的网络。位运算是发现网络广播地址所必需的。

处理IPv6地址需要相同的基本位级操作,但由于它们非常长,我不确定它们是如何实现的。我敢打赌,它们仍然使用适合架构的数据“片段”上的位运算符来实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接