欢迎投稿,推荐或自荐文章/软件/资源等,评论区留言
本期文章由 HNY lh_mouse 终盛 赞助
资讯
标准委员会动态/ide/编译器信息放在这里
七月邮件列表 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/#mailing2024-07
其中 3351 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3351r0.html
是群友Mick的提案
群友发的就等于大家发的,都是机会滋道吧
编译器信息最新动态推荐关注hellogcc公众号 本周更新 2024-07-10 第262期
ThinkCell发布了他们的C 26参会报告 Trip Report: Summer ISO C Meeting in St. Louis, USA
https://www.think-cell.com/en/career/devblog/trip-report-summer-iso-cpp-meeting-in-st-louis-usa
另外发现了一个好玩的网站 https://highload.fun/tasks/ 各种大数据场景 各种优化trick都可以用。感觉适合做面试题
另外发现一个关于高频低延迟开发的一本小书
C design patterns for low-latency applications including high-frequency trading
https://arxiv.org/pdf/2309.04259
代码在这里 https://github.com/0burak/imperial_hft
这里总结一下
- • cache warm
代码大概这样
代码语言:javascript复制#include <benchmark/benchmark.h>
#include <vector>
#include <algorithm>
constexpr int kSize = 10000000;
std::vector<int> data(kSize);
std::vector<int> indices(kSize);
static void BM_CacheCold(benchmark::State& state) {
// Generate random indices
for(auto& index : indices) {
index = rand() % kSize;
}
for (auto _ : state) {
int sum = 0;
// Access data in random order
for (int i = 0; i < kSize; i) {
benchmark::DoNotOptimize(sum = data[indices[i]]);
}
benchmark::ClobberMemory();
}
}
static void BM_CacheWarm(benchmark::State& state) {
// Warm cache by accessing data in sequential order
int sum_warm = 0;
for (int i = 0; i < kSize; i) {
benchmark::DoNotOptimize(sum_warm = data[i]);
}
benchmark::ClobberMemory();
// Run the benchmark
for (auto _ : state) {
int sum = 0;
// Access data in sequential order again
for (int i = 0; i < kSize; i) {
benchmark::DoNotOptimize(sum = data[i]);
}
benchmark::ClobberMemory();
}
}
测试数据直接快十倍,之前也讲过类似的场景
- • 利用模版和constexpr 这个就不多说了
- • 循环展开 这个也不说了
- • 区分快慢路径
- • 减少错误分支
- • prefetch
一个例子
代码语言:javascript复制#include <benchmark/benchmark.h>
#include <vector>
// Function without __builtin_prefetch
void NoPrefetch(benchmark::State& state) {
// Create a large vector to iterate over
std::vector<int> data(state.range(0), 1);
for (auto _ : state) {
long sum = 0;
for (const auto& i : data) {
sum = i;
}
// Prevent compiler optimization to discard the sum
benchmark::DoNotOptimize(sum);
}
}
BENCHMARK(NoPrefetch)->Arg(1<<20); // Run with 1MB of data (2^20 integers)
// Function with __builtin_prefetch
void WithPrefetch(benchmark::State& state) {
// Create a large vector to iterate over
std::vector<int> data(state.range(0), 1);
for (auto _ : state) {
long sum = 0;
int prefetch_distance = 10;
for (int i = 0; i < data.size(); i ) {
if (i prefetch_distance < data.size()) {
__builtin_prefetch(&data[i prefetch_distance], 0, 3);
}
sum = data[i];
}
// Prevent compiler optimization to discard the sum
benchmark::DoNotOptimize(sum);
}
}
BENCHMARK(WithPrefetch)->Arg(1<<20); // Run with 1MB of data (2^20 integers)
BENCHMARK_MAIN();
论文中快30%,当然编译器可以向量化的吧,不用手动展开吧
- • 有符号无符号整数比较,慢,避免
- • float double混用慢,避免
- • SSE加速
- • mutex替换成atomic (这个还是取决于应用场景)
- • bypass
还有其他模块介绍就不谈了,比较偏HFT
文章
C Error Handling Strategies – Benchmarks and Performance
浅析Cpp 错误处理
https://johnfarrier.com/c-error-handling-strategies-benchmarks-and-performance/
https://zhuanlan.zhihu.com/p/707442177
最近不约而同有两个关于错误处理的压测
第一个文章没有体验出正确路径错误路径不同压力的表现,只测了错误路径,因此没啥代表价值。只是浅显的说了异常代价大,谁还不知道这个
问题是什么情况用异常合适?异常不是你期待的东西,如果你的错误必须处理,那就不叫异常
另外第二篇文章是群友写的,给了个50%失败错误路径的测试,结果符合直觉
结论: 异常在happy path出现的路径下收益高(错误出现非常少)
当然已经有提案说过这个事情 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1886r0.html
我相信大多数人都没看过这个,是的我之前也没有看过
When __cxa_pure_virtual
is just a different flavor of SEGFAULT https://blog.the-pans.com/pure_virtual/
有时候可能会遇到这种打印挂掉pure virtual method called
一个简单的复现代码
代码语言:javascript复制class Base {
public:
Base() {fn();} // thinking it would be calling the Derived's `fn()`
// the same happens with dtor
// virtual ~Base() {fn();}
virtual void fn() = 0;
};
class Derived : public Base{
public:
void fn() {}
};
int main() {
Derived d;
return 0;
}
简单来说基类构造的时候子类还没构造,fn没绑定,还是纯虚函数,就会挂
不要这么写,不要在构造函数中调用虚函数
What’s the point of std::monostate? You can’t do anything with it!
https://devblogs.microsoft.com/oldnewthing/20240708-00/?p=109959
就是空类型,帮助挡刀的
比如这个场景
代码语言:javascript复制struct Widget {
// no default constructor
Widget(std::string const& id);
};
struct PortListener {
// default constructor has unwanted side effects
PortListener(int port = 80);
};
std::variant<Widget, PortListener> thingie; // can't do this
我们想让Widget当第一个,但是Widget没有默认构造,PortListener放第一个又破坏可读性,对应关系乱了
怎么办,monostate出场
代码语言:javascript复制std::variant<std::monostate, Widget, PortListener> thingie;
帮Widget挡一下编译问题
顺便一提,monostate的hash
代码语言:javascript复制 result_type operator()(const argument_type&) const {
return 66740831; // return a fundamentally attractive random value.
}
开源项目介绍
- • https://github.com/lhmouse/asteria 一个脚本语言,可嵌入,长期找人,希望胖友们帮帮忙,也可以加群753302367和作者对线
- • https://github.com/Psteven5/CFLAG.h/tree/main 一个类似GFLAGS的库,但是功能非常简单,口述一下原理
示例代码
代码语言:javascript复制int main(int argc, char **argv) {
char const *i = NULL, *o = NULL;
bool h = false;
CFLAGS(i, o, h);
printf("i=%s, o=%s, h=%sn", i, o, h ? "true" : "false");
}
// ./main -i=main.js -h -o trash
主要原理就是利用c11的_Generic
介绍一下generic用法
代码语言:javascript复制#include <math.h>
#include <stdio.h>
// Possible implementation of the tgmath.h macro cbrt
#define cbrt(X) _Generic((X),
long double: cbrtl,
default: cbrt,
float: cbrtf
)(X)
int main(void) {
double x = 8.0;
const float y = 3.375;
printf("cbrt(8.0) = %fn", cbrt(x)); // selects the default cbrt
printf("cbrtf(3.375) = %fn", cbrt(y)); // converts const float to float,
// then selects cbrtf
}
看懂了吧,_Generic根据输入能生成自定义的语句,上面的例子根据X生成对应的函数替换
能换函数,那肯定也能换字符串,这个关键字能玩的很花哨
回到我们这个flags,和Gflags差不多,我们怎么实现?
我们考虑一个最简单的场景 CFLAGS(i),应该展开成 解析arg遍历匹配字符串i并讲对应的值赋值给i,这个赋值得通过格式化字符串复制
遍历arg好实现,通过argc argv遍历就行,i字符串话也简单 #,把argv的值赋值, sscanf,格式化字符串哪里来?generic
大概思路已经有了,怎么实现大家看代码吧