C++ signed 和 unsigned int 与 long long 速度

标签 c++ performance bit-manipulation

今天,我注意到几个简单的按位和算术运算的速度在int之间有显着差异。 , unsigned , long longunsigned long long在我的 64 位电脑上。

特别是,对于 unsigned,以下循环的速度大约是其两倍至于long long ,这是我没想到的。

int k = 15;
int N = 30;
int mask = (1 << k) - 1;
while (!(mask & 1 << N)) {
    int lo = mask & ~(mask - 1);
    int lz = (mask + lo) & ~mask;
    mask |= lz;
    mask &= ~(lz - 1);
    mask |= (lz / lo / 2) - 1;
}

(完整代码 here )

以下是计时(以秒为单位)(对于 g++ -O-O2-O3):

1.834207723 (int)
3.054731598 (long long)
1.584846237 (unsigned)
2.201142018 (unsigned long long)

这些时间非常一致(即 1% 的差值)。 没有 -O flag,每个都慢一秒左右,但是相对速度是一样的。

这有明确的理由吗? 向量化可能适用于 32 位类型,但我看不到巨大的位置 long long之间的区别和 unsigned long long来自。 有些操作在某些类型上比在其他类型上慢得多吗? 还是 64 位类型较慢(即使在 64 位架构上)只是普遍现象?

对于那些感兴趣的人,这个循环遍历了 {1,2,...,30} 的所有子集。恰好有 15 个元素。这是通过循环(按顺序)所有小于 1<<30 的整数来完成的。恰好设置了 15 位。 对于当前情况,这是 155117520迭代。 我不知道这个片段的来源了,但是this帖子解释了更多。

编辑

从汇编代码看来,当类型是无符号时,除法可以更快。我认为这是有道理的,因为我们不必考虑符号位。

此外,32 位操作使用 movl及其他xxxl指示, 而 64 位操作使用 movqxxxq .

编辑2

阅读我链接的帖子后,我决定使用那里给出的公式:

T k = 15;
T N = 30;
T mask = (1 << k) - 1;
while (!(mask & 1 << N)) {
    T t = mask | (mask - 1);
    mask = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(mask) + 1));
}

它的运行时间大约是上面发布的代码的三分之一,并且对所有四种类型使用相同的时间。

最佳答案

你代码中最慢的操作是

mask |= (lz / lo / 2) - 1

32 位除法比 64 位除法快得多。例如,在 Ivy Bridge 上,32 位 IDIV 需要 19-26 个时钟,而 64 位 IDIV 需要 28-103 个时钟延迟。

无符号版本也比有符号版本快,因为在无符号情况下除以 2 只是简单的位移,并且没有大小扩展调用(CDQ、CQO)。

在无符号的情况下是简单的位移,而在有符号的情况下

[1] http://www.agner.org/optimize/instruction_tables.pdf

关于C++ signed 和 unsigned int 与 long long 速度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33848357/

相关文章:

c - 通过位操作将数据存储在变量中

c - C 中 float/double 的按位绝对值(转换过程中小数点丢失)

c - 这个用位运算计算 3/4*x 的程序怎么解释?

c++ - QMenuBar在Linux中不以窗体显示

c++ - 模板忽略 [[nodiscard]] 属性

oracle10g - 大数据集的分页? – 在一定时间后中止计数(*)

javascript - Angular2 提前 (AoT) 编译如何工作?

java - 取消对象所有字段的高效方法

c++ - 检查两个 Boost.MPL 序列是否以任何顺序包含相同的类型

c++ - 为 boost::shared_ptr<const Foo> 命名一个 typedef