gpt4 book ai didi

c++ - 为什么 GCC 减去 1 并比较 <= 2? cmp 在汇编中使用 2 的幂会更快吗?

转载 作者:行者123 更新时间:2023-12-05 03:23:37 24 4
gpt4 key购买 nike

我正在编写一些代码来将屏幕清除为特定颜色。 C++代码:

void clear_screen(unsigned int color, void *memory, int height, int width) {
unsigned int *pixel = (unsigned int *)memory;
for (auto y = 0; y < height; y++)
for (auto x = 0; x < width; x++)
*pixel++ = color;
}

我用g++和objconv生成了相应的程序集。这就是我得到的,并且我已经评论了我认为某些行也是如此。

renderer_clear_screen:
push r13
push r12
push rbp
push rdi
push rsi
push rbx
mov r11d, ecx ; move the color into r11d
mov ebx, r8d ; move the height into ebx
mov rcx, rdx ; 000E _ 48: 89. D1st
test r8d, r8d ;
jle _cls_return ; basically, return if width or height is 0
test r9d, r9d ; ( window minimized )
jle _cls_return ;
mov r8d, r9d ; height = width
mov esi, r9d ; esi = width
mov edi, r9d ; edi = width
xor r10d, r10d ; r10d = 0
shr esi, 2 ; esi = width / 2
movd xmm1, r11d ; move the lower 32-bits of the color into xmm1
lea r12d, [r9-1] ; r12d = width - 1
shl rsi, 4 ; 003F _ 48: C1. E6, 04
mov ebp, r8d ; 0043 _ 44: 89. C5
shl rdi, 2 ; 0046 _ 48: C1. E7, 02
pshufd xmm0, xmm1, 0 ; 004A _ 66: 0F 70. C1, 00
shl rbp, 2 ; 004F _ 48: C1. E5, 02

ALIGN 8
?_001: cmp r12d, 2
jbe ?_006 ; if (width - 1 <= 2) { ?_006 }
mov rax, rcx ; 005E _ 48: 89. C8
lea rdx, [rcx+rsi] ; 0061 _ 48: 8D. 14 31

ALIGN 8
?_002: movups oword [rax], xmm0 ; 0068 _ 0F 11. 00
add rax, 16 ; 006B _ 48: 83. C0, 10
cmp rdx, rax ; 006F _ 48: 39. C2
jnz ?_002 ; 0072 _ 75, F4
lea rdx, [rcx+rbp] ; 0074 _ 48: 8D. 14 29
mov eax, r8d ; 0078 _ 44: 89. C0
cmp r9d, r8d ; 007B _ 45: 39. C1
jz ?_004 ; 007E _ 74, 1C
?_003: lea r13d, [rax+1H] ; 0080 _ 44: 8D. 68, 01
mov dword [rdx], r11d ; 0084 _ 44: 89. 1A
cmp r13d, r9d ; 0087 _ 45: 39. CD
jge ?_004 ; 008A _ 7D, 10
add eax, 2 ; 008C _ 83. C0, 02
mov dword [rdx+4H], r11d ; 008F _ 44: 89. 5A, 04
cmp r9d, eax ; 0093 _ 41: 39. C1
jle ?_004 ; 0096 _ 7E, 04
mov dword [rdx+8H], r11d ; 0098 _ 44: 89. 5A, 08
?_004: add r10d, 1 ; 009C _ 41: 83. C2, 01
add rcx, rdi ; 00A0 _ 48: 01. F9
cmp ebx, r10d ; 00A3 _ 44: 39. D3
jnz ?_001 ; 00A6 _ 75, B0
_cls_return:
pop rbx ;
pop rsi ;
pop rdi ;
pop rbp ;
pop r12 ;
pop r13 ; pop all the saved registers
ret ;

?_006: ; Local function
mov rdx, rcx ; 00B1 _ 48: 89. CA
xor eax, eax ; 00B4 _ 31. C0
jmp ?_003 ; 00B6 _ EB, C8

现在,在 ?_001 中,编译器将 width - 12 进行比较,这与比较 是一回事宽度3。我的问题是,对于 -O3,为什么编译器选择了两个而不是三个,并浪费了一个 lea(将 width - 1 移动到r12d).
对我来说唯一有意义的是,两个的幂在某种程度上比较快。还是编译器的怪癖?

最佳答案

GCC 调整比较常量的通常原因是创建更小的立即数,这有助于它适应任何宽度的立即数。 Understanding gcc output for if (a>=3)/GCC seems to prefer small immediate values in comparisons. Is there a way to avoid that? (它总是这样做,而不是检查目标 ISA 上的这个常量是否真的有用。)这种启发式方法适用于大多数 ISA,但有时不适用于 AArch64 或 ARM Thumb,它们可以将一些立即数编码为位范围/位-pattern,所以并不总是数字越小越好。


width-1 不是其中的一部分。 -1range check 的一部分跳过自动矢量化循环(使用 movups 一次 16 个字节)并直接进行清理,1..3 标量存储。

好像是在检查width >= 1 && width <= 3 ,即需要清理但总大小小于完整 vector 宽度。它不等同于已签名或未签名的 width <= 3对于 width=0 .注意无符号比较:0 - 12U之上,因为 -1U是 UINT_MAX。

但它已经排除了width <= 0test r9d, r9d/jle _cls_return , 所以 GCC 最好只检查 width <= 3U而不是做额外的工作来从范围检查中排除零。 (一个 lea ,并保存/恢复 R12 未被使用!)

(清理也可能看起来过于复杂,例如,如果需要超过 1 个单位,则使用 movq [rdx], xmm0,并且在各种情况下使用一些奇怪的分支。甚至更好,如果总大小 >= 4 个单位,只需再做一个 movups,它在范围的末尾结束,可能与之前的商店重叠。)

是的,这是一个错过的优化,你可以在https://gcc.gnu.org/bugzilla/enter_bug.cgi?product=gcc上报告它(现在您知道这是一个错过的优化;最好先在这里询问,而不是在没有先弄清楚是否可以避免该指令的情况下提交错误。)


The only thing which makes sense to me is that powers of two are somehow faster to compare.

不,它并没有更快; cmp性能根本不依赖于数据。 (没有整数指令,除了有时 [i]div 。在 Zen3 之前的 AMD CPU 上, pext/pdep 。但无论如何,不​​是简单的整数加法/比较/移位的东西。见 https://uops.info/ )。


顺便说一句,我们可以重现您的 GCC asm output on Godbolt告诉它这个函数是__attribute__((ms_abi)) ,或者有一个命令行选项来设置调用约定默认值。 (它实际上只对查看 asm 有用;它仍在使用 GNU/Linux header 和 x86-64 System V 类型宽度,如 64 位 long 。只有合适的 MinGW(交叉)编译器才能向您展示 GCC 的真正用途在面向 Windows 时执行。)

这是煤气 .intel_syntax noprefix ,它类似于 MASM,而不是 NASM,但只有在涉及全局变量的寻址模式下,差异才会很明显。

关于c++ - 为什么 GCC 减去 1 并比较 <= 2? cmp 在汇编中使用 2 的幂会更快吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72482054/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com