以下来自我跟 DeepSeek 的对话:
我要为 C 语言的 qsort 编写一个比较函数,比较两个 uint64_t
,考虑以下两种实现:
int qcmp_uint64(const void* pa, const void* pb) {
uint64_t a = *(uint64_t*)pa;
uint64_t b = *(uint64_t*)pb;
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
int qcmp_uint64_2(const void* pa, const void* pb) {
uint64_t a = *(uint64_t*)pa;
uint64_t b = *(uint64_t*)pb;
return a - b;
}
从直觉上看,似乎第二种实现效率更高,但由于整数相减可能溢出,我对第二种方案的正确性产生了怀疑
第二种方案是否正确?如何高效实现这一比较函数?
对于第二种实现 qcmp_uint64_2
,直接返回 a - b 的确存在隐患。原因如下:
无符号整数减法不会溢出,但结果始终为非负数。
转换为 int 可能导致符号错误:
例如:
高效且安全的实现 利用 比较操作的布尔值隐式转换为 0/1 ,可写出无分支的高效代码:
int qcmp_uint64(const void* pa, const void* pb) {
uint64_t a = *(const uint64_t*)pa;
uint64_t b = *(const uint64_t*)pb;
return (a > b) - (a < b); // 若 a > b 则 1-0=1 ; a < b 则 0-1=-1 ;相等则为 0
}
优点:
性能验证
在 x86-64 架构下,上述代码的典型汇编输出为:
mov rax, [rdi]
cmp [rsi], rax
setb al
seta cl
movzx eax, al
movzx ecx, cl
sub eax, ecx
ret
完全无分支,效率与减法方案相当,且绝对安全。
结论:不要使用 a - b 方案,优先选择无分支的布尔比较实现。
这个 (a > b) - (a < b) 的写法太巧妙了!
感觉这类惯用法( idiom )很难通过看书或网络学习,正是 AI 的强项
只搜到一个相关的: https://stackoverflow.com/questions/3886446/problem-trying-to-use-the-c-qsort-function
1
jinsongzhaocn 23 小时 58 分钟前
确实巧妙,AI 辅助编程是趋势,让 AI 收集优秀的算法,比人快。
|
2
kaedeair 23 小时 35 分钟前
开 O1 优化在 clang 下第一种方案和 ai 给出的方案得到的汇编是一致的,但是 gcc 就不行
|
![]() |
3
Shatyuka 23 小时 30 分钟前
|
4
arielherself 23 小时 28 分钟前
在我看来之所以一般不会学到这种写法,是因为平时写程序没有到苛求比较函数性能的地步。而且楼上也说了编译器优化可以处理好这种问题。
1. 如果目的是学习 C 语言,则完全没必要知道这个; 2. 如果目的是编写高性能的程序,可能会先去检查程序的性能瓶颈,然后调查不同排序算法之间的性能差异,最后才看到这里可以优化的地方; 3. 如果是对各种 trick 感兴趣,也许多泡一泡各种社区会有帮助。 |
![]() |
5
AFOX 20 小时 26 分钟前
本质上只是把这一段优化成了一行代码:
if (a < b) return -1; if (a > b) return 1; return 0; |
6
bbao 20 小时 18 分钟前 ![]() 就 up 这个例子而言:
1 ,所谓的性能,运行无数次也不会造成性能瓶颈,也不会额外提升多少性能指标;无意义优化 2 , [return (a > b) - (a < b); // 若 a > b 则 1-0=1 ; a < b 则 0-1=-1 ;相等则为 0 ] 就这段逻辑而言,如果不加注释,可读性没有第一种来的直观,而且非常规写法,不影响性能的情况下,「可读性」为第一考虑要义 总结:不建议 |
9
craftsmanship 19 小时 53 分钟前 via Android ![]() 楼主掉坑里去了 当年刷 LC 的时候我也喜欢总结这种看上去很聪明的奇技淫巧 然而在实际工程里面这么写 绝对会被打死 像楼上各位所言 毫无意义 会喜欢这种写法只能说明 缺乏阅读足够量的优秀代码
|
10
henix OP 我本来是想发出来看看有没有人能发散一下这种写法的历史,或者在其他知名项目中看到过之类的……因为这么底层(常用)的,而且又很漂亮的写法,我之前竟然完全不知道……
我又找了一下,只在 stackoverflow 上找到两个帖子提到: * https://stackoverflow.com/questions/49663753/qsort-function-compare-confused-me * https://stackoverflow.com/questions/3886446/problem-trying-to-use-the-c-qsort-function 外国人都说是“common idiom”,说明这种写法确实是一种惯用法,但他们没有指出是谁最先这么用。如果是惯用法,个人认为并不会对可读性造成较大伤害 而且我观察到 gcc 和 msvc 这两种编译器默认都无法将我的第一个比较函数优化成第三种,只有 clang 可以,可见这种写法确实能产生更优的编译结果,我认为还是很有意义的 P.S. 发散一下:上面的第一个链接指出如果是比较两个 int 也不能直接相减,而且这种直接相减的写法出自《 The C Programming Language 》第五章 |
11
MrKrabs 15 小时 11 分钟前
实际用在排序函数里就会优化成一样的了
|