32 位比较比 64 位比较快吗?

Is 32 bit comparison faster than 64 bit comparison?

提问人:Sayan 提问时间:4/16/2021 最后编辑:user16217248Sayan 更新时间:5/23/2023 访问量:354

问:

32 位的比较比 64 位的比较快吗?

我正在看这个文件 http://www.netlib.org/fdlibm/s_cos.c

他们有这段代码

    /* |x| ~< pi/4 */
    ix &= 0x7fffffff;
    if(ix <= 0x3fe921fb) return __kernel_cos(x,z);

我理解第一行,它计算 x 的绝对值。但为什么比较如此复杂呢?通过比较前 32 位而不是所有 64 位,性能是否有任何改进?我可以写吗

long unsigned int ix = *(long unsigned int *)(&x);
ix &= 0x7fffffffffffffff;
if (ix < 0x3fe921fb54442d18) 
/* What comes next */ ;

并期望在 64 位机器上获得相同的速度性能?虽然我同意这会消耗更多的内存。

0x3fe921fb54442d18是 pi/2。

C 性能 比较 微优化

评论

0赞 OrangeDog 4/16/2021
首先要注意的是,这是一个.位模式与整数不同。比较可能更快,也可能不更快,但这与此代码无关。xdouble
4赞 Basile Starynkevitch 4/16/2021
这取决于目标体系结构和处理器。
1赞 Adrian Mole 4/16/2021
自从编写该代码(1995 年)以来,情况可能已经发生了变化。在现代芯片上,直接测试可能(几乎)同样有效。fabs(x)
0赞 Irelia 4/16/2021
无论大小,原子操作可能不会对速度产生影响。比较 eax,edx 的速度与比较 rax,rdx 的速度相同,尽管 rax,rdx 实际上可能更快,因为 64 位芯片的内部流水线。
2赞 MSalters 4/16/2021
正如 Adrian Mole 所指出的,这是非常古老的代码(版权所有 1993)。特别是,它比 64 位 CPU 要古老得多。出于显而易见的原因,在 32 位 CPU 上比较 64 位确实更慢。

答:

0赞 user16217248 5/23/2023 #1

在我的 64 位 Intel 机器上,我尝试运行多次输入超过 10 亿次的地方。然后我尝试用相同的次数输入跑步。以下是以秒为单位的结果:Apple clang version 12.0.0ix <= 0x3fe921fbixintix < 0x3fe921fb54442d18unsigned longix

无优化 32 位:

1.470922
1.448247
1.449718
1.446084
1.450020
1.453608

无优化 64 位:

1.567637
1.561653
1.565024
1.575094
1.567794
1.564141

-O332 位:

0.421903
0.419469
0.425281
0.419894
0.425790
0.424800

-0364 位:

0.636965
0.640522
0.637279
0.634344
0.634989
0.633755

至少在我的机器上,32 位比较始终略快。

我会选择第一种选择。即使在 64 位机器上,无论优化设置如何,速度都更快,并且使用更少的内存和功耗,因为 32 位比较电路更小,使用更少的能量。另请注意,使用键入在技术上是未定义的行为long unsigned int ix = *(long unsigned int *)(&x);doublex

测试代码:

volatile int ix = 0; // Changing this value has no effect
clock_t before = clock();
for (int i = 1<<30; i--;) {
    volatile int sink = ix <= 0x3fe921fb;
}
printf("%f\n", (double)(clock()-before)/CLOCKS_PER_SEC);
volatile unsigned long ix = 0;
clock_t before = clock();
for (int i = 1<<30; i--;) {
    volatile int sink = ix < 0x3fe921fb54442d18;
}
printf("%f\n", (double)(clock()-before)/CLOCKS_PER_SEC);

评论

1赞 Peter Cordes 5/23/2023
你使用了什么测试代码,是如何编译的?或者你是用 asm 手写的,这样你就可以比较优化编译器一次可能做什么?最大的区别是,将 64 位常量放入寄存器中需要更多/更大的指令,例如(x86-64 上的 10 字节),而 32 位即时可直接用作(或针对 32 位内存操作数)的即时操作。mov rax, 0x3fe921fb54442d18cmp ecx, 0x3fe921fb
1赞 Peter Cordes 5/23/2023
但是,是的,在 x86-64 上,如果可能的话,32 位操作数大小通常更好。即使没有任意常量,代码大小也略小。在 x86-64 中使用 32 位寄存器/指令的优点。这在 AArch64 或大多数其他 64 位 ISA 上并非如此,除非您需要具体化 64 位常量,否则代码大小没有区别。我假设无论您如何编译测试,编译器都无法将常量放在循环之外的寄存器中?
1赞 Peter Cordes 5/23/2023
好的,这允许编译器将 64 位常量从循环中提升出来;godbolt.org/z/oes86r5W4 显示每个比较的工作是加载 64 位易失性 , 比较,并将结果布尔化为 32 位 0 或 1(这在 x86 上并非易事,不幸的是,除了制作 8 位 0/1 之外,还需要 2 条额外的指令)。ix
1赞 Peter Cordes 5/23/2023
无论如何,由于它可以将常量提升出循环,并且没有来自代码大小的 I-cache 压力,我希望两个循环以相同的速度运行,除了代码对齐的怪癖(尤其是在 Skylake 上,如果您不使用 如何减轻英特尔 jcc 勘误表对 gcc 的影响?)
1赞 Peter Cordes 5/23/2023
是的,只有少数架构(特别是 MIPS 和 RISC-V)具有比较指令,这些指令可以比较通用整数寄存器中的 0/1 整数。x86 有一个指令 () 根据 FLAGS 条件写入一个 8 位寄存器或带有 0/1 的内存位置,但将其扩展到 32 位需要另一条指令,最好在进行比较之前对整个寄存器进行异或归零。AArch64 要好得多,能够在零注册上使用灵活且设计巧妙(条件选择 inc)的 insn 在另一条指令中将比较结果具体化为 0/1。setcccsinc