为何手写memcmp无法被编译器优化为单条cmpl指令?
为什么自定义memcmp无法被编译器优化为单条比较指令?
标准库memcmp的优化效果
当使用标准库的memcmp时,编译器可以将代码优化为单条32位整数比较指令:
#include <string.h> bool test_data(void *data) { return memcmp(data, "abcd", 4) == 0; }
编译生成的汇编:
test_data: cmpl $1684234849, (%rdi) sete %al ret
自定义memcmp的编译结果
但如果替换为自己实现的memcmp,编译器只能生成逐字节比较的汇编,无法合并为单条整数比较指令:
static int memcmp(const void *s1, const void *s2, size_t n) { const unsigned char *p1 = s1, *p2 = s2; size_t i; for (i = 0; i < n; i++) { int ret = p1[i] - p2[i]; if (ret) return ret; } return 0; } bool test_data(void *data) { return memcmp(data, "abcd", 4) == 0; }
编译生成的汇编:
test_data: cmpb $97, (%rdi) jne .L5 cmpb $98, 1(%rdi) jne .L5 cmpb $99, 2(%rdi) jne .L5 cmpb $100, 3(%rdi) sete %al ret .L5: xorl %eax, %eax ret
阻碍优化的核心原因
- 标准库函数的内置语义支持:编译器对标准库的
memcmp有特殊处理,内置了它的完整语义模型——知道它是逐字节比较无符号字符,且当比较长度为4时,直接按32位整数比较的结果和逐字节比较完全等价(小端系统下,"abcd"对应的整数就是各字节的拼接值)。因此编译器可以直接替换为更高效的整数比较指令。 - 自定义函数的语义推导限制:你写的自定义
memcmp虽然行为和标准库一致,但编译器无法仅凭代码就确认它和标准memcmp的语义完全等价。优化器需要证明循环展开后的逐字节比较,与整数比较的结果完全相同,这种跨类型的等价转换证明对用户自定义函数来说,编译器的启发式规则不会主动触发,因为它需要额外的语义信息,而这些信息只有标准库函数才会被编译器预先赋予。 - 代码结构的影响:自定义函数的逐字节循环返回结构,会让编译器先展开循环为4次独立的字节比较,但不会进一步合并为整数比较。因为编译器默认不会对用户代码做这种“语义等价替换”,除非你通过编译属性(比如
__attribute__((memcmp)))明确告诉编译器这个函数的语义和标准memcmp一致。
内容的提问来源于stack exchange,提问作者Ammar Faizi
相关产品推荐
相关产品推荐

