C++ 中文周刊 2025-05-19 第183期

周刊项目地址

公众号

点击「查看原文」跳转到 GitHub 上对应文件,链接就可以点击了

qq群 753792291 答疑在这里

RSS

欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言


资讯

标准委员会动态/ide/编译器信息放在这里

编译器信息最新动态推荐关注hellogcc公众号 本周更新 2025-01-08 第288期

性能周刊

文章

C++26: constexpr exceptions

优化异常constexpr化,不能constexpr的编译期报错,加固代码降低UB,同时降低异常开销,好事

C++20 concepts for nicer compiler errors

concept编译模版报错更易懂

An option(al) to surprise you

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')

实际上性能并没有比无分支版本强多少,另外使用场景也不会特别随机。无分枝版本就挺好

The case of the UI thread that hung in a kernel call

不要在进程内挂起线程。自己给自己搞死锁。感谢 YexuanXiao投稿

Load-store conflicts

非常好的文章 多版本编译器压测对比才发现问题所在

数据存在存储加载冲突,导致性能瓶颈,这种通过汇编是看不出来的,得对比压测才能看出来

视频

File IO - Past, Present and Future - Guy Davidson - ACCU 2024

大部份都在讲历史,然后讲一段#embed 和提案相关的file handle之类的设计

Understanding the Filter View to Use It Right - Nicolai M. Josuttis - ACCU 2024

filter view由于缓存性质多了很多坑,主要是讲使用细节

Narrow Contracts and noexcept are Inherently Incompatible in C++ - John Lakos - ACCU 2024

Narrow contract和noexcept不兼容

什么?你不知道narrow contract是什么?翻出contract概念复习一下

开源项目介绍

互动环节

群聊总会在没注意的时候发生吵架,如何避免这种情况?群聊知识如何更好的沉淀?

我的机器人还没有搞好


上一期

本期

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账