【问题标题】:What is faster? Bitwise operations or Array operations. Why?什么更快?按位运算或数组运算。为什么?
【发布时间】:2018-08-03 11:34:06
【问题描述】:

假设有一排 10 个灯泡。它们的状态可以是 ON (1) 或 OFF(0)。

这可以用一个数组来表示。

states = [1, 0, 1, 1, 0, 1, 0, 0, 1, 0]

或作为 10 位数字。

states = 1011010010

更新状态(在列表中设置值):

对于数组来说是

states[0] = 1

对于位,它将使用位掩码

states = states | 10000000

类似的操作将用于清除位等。

这些操作中哪个更快?哪些情况下应该使用数组,位也应该使用?

我已经看到某些引擎(例如国际象棋/跳棋引擎)使用位串来存储棋盘状态(1 = 方块上的棋子,0 = 方块上的棋子)。有些还使用数组。如果可能,也可以考虑这种情况。

【问题讨论】:

  • 使用位掩码应该更快,因为对简单整数执行操作比执行数组访问算术更快。您必须小心整数类型可以包含您的所有位。
  • 我在@Adder 评论上 +1,但想指出任何可以包含在机器寄存器中的东西都会更快。然而;如果状态列表超出该边界,您将需要执行一次提取(可能在缓存中,也可能不在缓存中),这会降低性能。
  • 没有一般的答案。使用数组可以锻炼内存子系统,使用位可以让处理器内核做更多的工作。机器的不同部件,唯一确定的方法就是尝试一下。

标签: arrays performance bit-manipulation programming-languages


【解决方案1】:

这些操作中哪个更快?

假设我们有N 标志,其中N 是一个很大的数字。让我们比较一下为了测试标志需要做什么。

标志操作数组:

每个flag是一个字节,所以数组的大小是N字节。

byte states[N];
sizeof (states) == N;
  1. 访问数组,即value = *(&states + index
  2. value 与零进行比较。

位操作:

每个flag是一个位,每8个位被分组为字节。所以数组的大小是N / 8字节。

byte states[N / 8];
sizeof (states) == N / 8;
  1. 计算数组索引,即arr_idx = index / 8
  2. 访问数组元素,即value = *(&states + arr_idx
  3. 计算位索引,即bit_idx = index % 8
  4. 测试value 中的位bit_idx 是否为零。

正如我们所见,位运算需要更多的步骤。但是 CPU 擅长处理这些额外步骤所需的两个操作,因此从性能的角度来看,它会稍微慢一点。

在哪些情况下应该使用数组,对于位也是如此?

我们更喜欢位来存储大量的标志。每个字节都有8 位,因此位的内存占用将始终是8 的倍数。

为了简单、速度和原子操作,我们更喜欢字节。大多数 CPU 都有特殊的“原子”指令来原子地测试/设置/清除字节,但不是字节中的位。

例如,如果在应用程序中的几个线程之间使用了滞后数组,我们可以使用原子 CPU 指令对该数组进行操作。对于位,它会有点复杂,有锁或比较交换循环。

【讨论】:

  • 为什么要计算位的数组索引?
  • @hashris 我的意思是当我们有很多标志时,即当它不适合一个 state 时,所以我们需要保留一个 states 数组。对于您描述的最简单的情况,不需要它。
【解决方案2】:

在现代处理器(x86、ARM 等)上,寄存器的按位操作(最多)需要一个时钟周期。实际上远不止这些,因为这些类型的操作使用 CPU 中一些最简单的单元,它们能够在单个周期内执行和退出多个操作(例如 Intel CPU),因此汇编程序员认为这些操作是免费的。

另一方面,数组处理需要内存加载和存储,并且可能超过缓存行大小,如果缓存未命中,这会造成很大的延迟。

真的没有可比性。位运算是免费的;数组操作相对昂贵。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多