admin管理员组文章数量:1440030
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++缓存本文标签: C 中文周刊 2025
版权声明:本文标题:C++ 中文周刊 2025 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/biancheng/1747701371a2746532.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论