最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

C++ 中文周刊 2025

网站源码admin3浏览0评论

C++ 中文周刊 2025

资讯

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

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

性能周刊

文章

Announcing Guidelines Support Library v4.2.0

主要改动就是gsl span性能提升,以前相比std::span很慢(边界检查)

How can I choose a different C++ constructor at runtime?

考虑我们想要调用不同的基类

看代码

代码语言:javascript代码运行次数:0运行复制
struct WidgetBase
{
    // local mode
    WidgetBase();

    // remote mode
    WidgetBase(std::stringconst& server);

    // The mutex makes this non-copyable, non-movable
    std::mutex m_mutex;
};

struct WidgetOptions
{
    ⟦ random stuff ⟧
};

struct Widget : WidgetBase
{
    Widget(WidgetOptions const& options) :
        // 不好使                
        CanBeLocal(options)                 
            ? WidgetBase()                  
            : WidgetBase(GetServer(options))
    {}

    static bool CanBeLocal(WidgetOptions const&);
    static std::string GetServer(WidgetOptions const&);
};

怎么搞?通过create函数+返回值优化来转发

代码语言:javascript代码运行次数:0运行复制
struct Widget : WidgetBase
{
    Widget(WidgetOptions const& options) :
        WidgetBase(ChooseWidgetBase(options))
    {}

    static bool CanBeLocal(WidgetOptions const&);
    static std::string GetServer(WidgetOptions const&);

private:
    static WidgetBase ChooseWidgetBase(           
        WidgetOptions const& options)             
    {                                             
        if (CanBeLocal(options)) {                
            return WidgetBase();                  
        } else {                                  
            return WidgetBase(GetServer(options));
        }                                         
    }                                             
};

Troubleshooting between C++ Module and NVCC

分享一下失敗的經驗, 還想不到什麼好方法讓Module跟非Module溝通..

目前不推荐使用module

Improving on std::count_if()'s auto-vectorization

他的场景是这样的,检查一组uint8数组判断偶数个数,并且已经确认偶数在0-255之间

简单代码示这样的

代码语言:javascript代码运行次数:0运行复制
auto count_even_values_v1(const std::vector<uint8_t> &vec)
{
    return std::count_if(
        vec.begin(),
        vec.end(),
        [](uint8_t x) { return x % 2 == 0; }
    );
}

观察汇编

代码语言:javascript代码运行次数:0运行复制
.LCPI0_1:
    .byte   1
count_even_values_v1():
;   ............
    vpbroadcastb    xmm1, byte ptr [rip + .LCPI0_1]
.LBB0_6:
    vmovd   xmm2, dword ptr [rsi + rax]
    vpandn  xmm2, xmm2, xmm1
    vpmovzxbq       ymm2, xmm2
    vpaddq  ymm0, ymm0, ymm2
    add     rax, 4
    cmp     r8, rax
    jne     .LBB0_6

不知道为什么扩展成64位了。

原因在于countif的返回值difference_type,

但我们结果明显小于255,可以改成uint8

所以代码改成这样

代码语言:javascript代码运行次数:0运行复制
template<typename Acc, typename It, typename Pred>
Acc custom_count_if(It begin, It end, Pred pred)
{
    Acc result = 0;
    for (auto it = begin; it != end; it++)
    {
        if (pred(*it))
            result++;
    }
    return result;
}

auto count_even_values_v2(const std::vector<uint8_t> &vec)
{
    return custom_count_if<uint8_t>(
        vec.begin(),
        vec.end(),
        [](uint8_t x) { return x % 2 == 0; }
    );
}

再看汇编

代码语言:javascript代码运行次数:0运行复制


.LCPI0_2:
    .byte   1
count_even_values_v2():
;   ............
    vpbroadcastb    ymm1, byte ptr [rip + .LCPI0_2]
.LBB0_11:
    vmovdqu ymm2, ymmword ptr [rdx + rax]
    vpandn  ymm2, ymm2, ymm1
    vpaddb  ymm0, ymm2, ymm0
    add     rax, 32
    cmp     rdi, rax
    jne     .LBB0_11

可以看到之前的汇编是dword

每次处理4个8位值(dword),通过vpandn提取最低位并取反,再通过vpmovzxbq将结果零扩展为64位累加

现在是ymmword

每次处理32个8位值(ymmword),使用vpaddb直接累加8位结果

测试显示速度快两倍以上 godbolt

这个例子让我想起之前聊过的64*64计算问题

如果计算64 x 64 -> 128 不要提前转128 计算途中转就可以,编译器知道你想干嘛,提前转128会导致编译器认为你想生成256的数去截断 128,导致多余的mul

能确定结果集的优化会更立竿见影

Advanced C++ Optimization Techniques for High-Performance Applications — Part 1

讲了分支预测like/unlike,缓存优化,SIMD

缓存优化讲了

  • 局部性
  • prefetch
  • cache分块 loop tiling
    • 基本上BLAS库都会有这个优化,LLM场景有一个flashattention,同样的原理

一个loop tiling举例

代码语言:javascript代码运行次数:0运行复制
const int BLOCK = 1024;
for (int start = 0; start < N; start += BLOCK) {
    int end = std::min(start + BLOCK, N);
    for (int i = start; i < end; ++i) {
        result[i] += compute(A[i], B[i]);
    }
}

Advanced C++ Optimization Techniques for High-Performance Applications — Part 2

讲了循环展开/向量化,函数内联和指令缓存效应,返回值优化,链接优化(LTO/WPO)内存对齐/填充,SOA/AOS

  • 循环展开通常没啥用,O3会帮你做。真要做,测试
  • 函数内联,可能加快,但是可能二进制膨胀
    • 注意指令缓存icache L1i 32k 热点代码做了反而会溢出导致触发指令读,需要观察itlb miss l1i miss

其他的没啥说的,讲过多次

Advanced C++ Optimization Techniques for High-Performance Applications — Part 3

讲了无锁编程,线程亲和,numa

false sharing问题,对象在同一个内存行导致互相影响

比如

代码语言:javascript代码运行次数:0运行复制
struct PaddedCounters {
    alignas(64) std::atomic<int> c1;
    alignas(64) std::atomic<int> c2;
};

其他没啥有用的东西

Performance Engineering — Part 1

检查过多的上下文切换,如何判定

  • vmstat 看cs 万级别
  • pidstat 看 nvcswch/nivcswch 资源切换/非自愿切换
  • top/htop看负载
  • perf stat -a -e cs -e migrations -e faults

缓解

  • 线程池
  • 亲和性

检测内存碎片

  • vmstat si/so
  • /proc/meminfo Slab数量 MemAvailable多但MemFree少
  • smem -t
  • cat /proc/buddyinfo 看高等级空闲块数量,没有说明碎片多
  • perf record观察 compact_zone migrate_pages

缓解

  • 调整分配器的配置
  • 内存池
  • malloc_trim
  • 换容器,vector -> deque

Polymorphic, Defaulted Equality

案发现场

代码语言:javascript代码运行次数:0运行复制
struct Base {
    virtual ~Base() = default;
    virtualautooperator==(Base const&) const -> bool = 0;
};

struct Derived : Base {
    int m1, m2;
    booloperator==(Base const& rhs) constoverride {
        if (typeid(rhs) == typeid(Derived)) {  // 确保类型严格匹配
            return *this == static_cast<Derived const&>(rhs); // 调用默认比较
        }
        returnfalse;
    }
    booloperator==(Derived const&) const = default;  // 默认生成
};

默认生成会生成

代码语言:javascript代码运行次数:0运行复制
auto Derived::operator==(Derived const& rhs) const -> bool {
    return static_cast<Base const&>(*this) == static_cast<Base const&>(rhs)
       and m1 == rhs.m1
       and m2 == rhs.m2;
}

调用基类,基类再调用子类,无限循环

解决方法就是这种接口(operator ==)别虚函数

Bypassing the branch predictor

考虑这么个场景

代码语言:javascript代码运行次数:0运行复制
struct Transaction;
bool should_send(Transaction *t);
void send(Transaction *t);
void abandon(Transaction *t);

void resolve(Transaction *t)
{
    if (should_send(t)) {
        send(t);
    } else {
        abandon(t);
    }
}

在金融交易系统中,大部分交易请求会被放弃(abandon()),仅有少数需要发送(send())。由于分支预测器倾向于预测进入高频路径(abandon()),当实际需要执行低频的 send() 时,会导致以下性能问题

  • 分支预测错误:产生约20个时钟周期的惩罚。
  • 指令缓存未命中:send() 相关代码因执行频率低,可能未被加载到指令缓存。
  • 流水线中断:无法预取 send() 的后续指令。

这种场景是likely这种东西无法搞定的,需要的是硬件写死的那种需求

不是说likely不行,你加上likely没法改变运行状态中分支预测器的行为,只是改变汇编

有一种玩法就是cache warm,长时间跑假的执行的数据骗他。不过CPU肯定上升就是了

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。原始发表:2025-04-13,如有侵权请联系 cloudcommunity@tencent 删除编译器内存优化c++缓存

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论