公众号
点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了
qq群 753792291 答疑在这里
欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
标准委员会动态/ide/编译器信息放在这里
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2025-01-08 第288期
优化异常constexpr化,不能constexpr的编译期报错,加固代码降低UB,同时降低异常开销,好事
concept编译模版报错更易懂
enum class State {
Initial,
WaitingForInput,
GotValidInput,
GotValidInputWithData,
GotInvalidInput
};
std::optional<const char*> Worker(State s, const char* data) {
switch(s) {
using enum State;
case Initial: return std::nullopt;
case WaitingForInput: return {};
case GotValidInput: [[fallthrough]];
case GotValidInputWithData: return data;
case GotInvalidInput: return "";
}
return std::nullopt;
}
幽默代码 这个例子里 optional有点类似optinal<T*> 存指针也就有可能存nullptr,导致误用报错。坏实践
传统算法
bool is_leap_year(uint32_t y) {
if ((y % 4) != 0) return false;
if ((y % 100) != 0) return true;
if ((y % 400) == 0) return true;
return false;
}
显然,能继续优化
bool is_leap_year1(uint32_t y) {
if ((y % 4) != 0) return false;
if ((y % 25) != 0) return true;
if ((y % 16) == 0) return true;
return false;
}
我们考虑去掉mod
bool is_leap_year2(uint32_t y) {
if ((y & 3) != 0) return false;
if (y * 3264175145u > 171798691u) return true;
if ((y & 15) == 0) return true;
return false;
}
第二行有点令人费解 我们考虑通过定点缩放来去掉mod
我们需要找到一个分数来近似表示 1/25
在32位无符号整数中,我们可以用 m/2^32 来近似 1/25
通过计算得知 2^32 / 25 = 171798691.84 所以我们取 m = 171798692 作为近似值 因此,对于任意数x:
(x * 171798692) / 2^32 近似等于 x/25 如果x是25的倍数,则结果是一个整数 如果x不是25的倍数,则结果有小数部分 在32位计算中,乘法 x * 171798692 的高32位实际上就是 (x * 171798692) / 2^32
要检查x是否为25的倍数,我们需要判断这个结果是否为整数,但实际上我们要的是相反的结果(不是25的倍数)
通过将问题转化为检查 x * 3264175145u > 171798691u,我们实际上在检查:
3264175145是2^32 - 171798692 + 1的补码 这个比较操作效果上等同于检查x不是25的倍数 为什么用3264175145而不是171798692?
这里的关键是编译器优化中使用的技巧。不是直接计算 x * 171798692 / 2^32,而是通过计算 x * (2^32 - 1) / 25 + 1 来达到同样的效果。
具体计算: (2^32 - 1) / 25 = 171798691.96 取近似值为3264175145 当我们用x乘以这个数时,对于25的倍数,结果恰好落在某个特定范围内,使得 x * 3264175145u > 171798691u 正好对应 x % 25 != 0
但是这种编译器也能优化,属于过度优化,我们考虑分支优化
bool is_leap_year3(uint32_t y) {
return !(y & ((y % 25) ? 3 : 15));
}
性能比上面的要好
作者考虑第二种算术优化和分支优化的结合版本
作者是用求解器硬算的,考虑构造 ((y * f) & m) <= t格式,然后求解器算出满足条件的f m t
import z3
BITS = 32
f, m, t, y = z3.BitVecs('f m t y', BITS)
def target(y):
return z3.And((y & 3) == 0, z3.Or(z3.URem(y, 25) != 0, (y & 15) == 0))
def candidate(x):
return z3.ULE((x * f) & m, t)
solver = z3.Solver()
solver.add(z3.ForAll(y, z3.Implies(z3.ULE(y, 400),
candidate(y) == target(y))))
if solver.check() == z3.sat:
print(f'found solution: {solver.model()}')
else:
print('no solution found')
实际上性能并没有比无分支版本强多少,另外使用场景也不会特别随机。无分枝版本就挺好
不要在进程内挂起线程。自己给自己搞死锁。感谢 YexuanXiao投稿
非常好的文章 多版本编译器压测对比才发现问题所在
数据存在存储加载冲突,导致性能瓶颈,这种通过汇编是看不出来的,得对比压测才能看出来
大部份都在讲历史,然后讲一段#embed 和提案相关的file handle之类的设计
filter view由于缓存性质多了很多坑,主要是讲使用细节
Narrow contract和noexcept不兼容
什么?你不知道narrow contract是什么?翻出contract概念复习一下
群聊总会在没注意的时候发生吵架,如何避免这种情况?群聊知识如何更好的沉淀?
我的机器人还没有搞好