比较有符号整数比无符号整数更快

Faster comparing signed than unsigned ints

本文关键字:无符号整数 整数 符号 比较      更新时间:2023-10-16

可能重复:
无符号整数与有符号整数的性能

我在某个地方读到,在x86_64上比较C/C++中的signed intsunsigned ints快一点,例如for (int i...)比for (uint i...)"快"。

这是真的吗?为什么是真的?我知道差别非常小,但无论如何。

你最好引用这样一个说法的来源,从表面上看这很荒谬。

我们谈论的是x86_64,它意味着现代处理器。这些ALU将在单个时钟周期内完成整数加法、减法和/或比较(缓存未命中当然需要更长的时间,但仅取决于数据的大小和内存布局,而不是符号)。甚至更少,使用SIMD协处理器。

我更可能相信这两种类型的比较之间有轻微的功率差异,但没有速度差异。

现在,对于特定的编译器,在针对x86_64平台时,一种数据类型的代码生成可能比另一种更差。但这将是一个非常特殊的情况,不太可能适用于所有x86_64编译器。尽管如此,我还是怀疑缓存效应或后台进程会影响性能测量(即使是测量每个进程所花费时间的性能计数器也会受到上下文切换使缓存无效的影响)。

这里没有好的答案。速度差异通常是微不足道的,所以你可以通过花时间思考其他事情来获得更好的表现。但也有一些奇怪的情况,因为签名溢出是未定义的。例如,比较这两个:

for (int i = 0; condition(); ++i) {
    if (i == 0) {
        computation();
    }
}
for (unsigned i = 0; condition(); ++i) {
    if (i == 0) {
        computation();
    }
}

一致的编译器可以将computation移到使用有符号索引的循环之外,因为它可以假设i == 0一次且仅一次——因为有符号溢出是未定义的行为(溢出可能会终止程序,或使其循环,或使恶魔飞出你的鼻子)。但是,如果没有更多的工作,编译器就无法将computation移动到第二个循环之外(无符号整数溢出时总是换行)。

但是,在x86_64上,在使用int之前,通常必须对其进行签名扩展。这需要额外的指令。

结论:这其实并不重要。无论谁告诉你一个比另一个快,都会分散你的注意力。

也许在某些情况下,有符号整数会迫使编译器对它们进行符号扩展
如果将整数移动到eax,则rax的高位将设置为零(低位为eax的位)。如果是整数,则需要将高32位设置为低32位的符号。这是一个额外的指示。

我不确定在简单的for (i=0; i<MAX; i++)中是否需要这个符号扩展。

速度应该没有差异。x86 32/64上的比较指令(CMP)在有符号和无符号整数数据类型上都是相同的。所有的分支指令(jxx)和条件移动(cmovxx)都是通过测试CMP更改的CPU标志来工作的。整数数据的增、减、加、减也不知道有符号或无符号数据类型(由于2补码)。

测量

平台上的测量一直是回答此类问题的唯一方法(x86_64只是确定平台的起点,可能需要精确的模型)。现今在芯片上进行的优化(看看"具有推测执行的超标量无序处理器"是什么意思)和编译器进行的优化产生了这种差异(假设它们存在,我并不真的认为这是有符号/无符号比较,但值范围传播和强度降低等优化可能会产生影响),因此取决于上下文上下文是必要的,因为它会对结果产生影响(我有一段代码,其中将一些整数变量作为双精度变量会使其在某些机器上表现更好,而在其他机器上表现更差)。