前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 中文周刊 2024-07-21 第164期

C++ 中文周刊 2024-07-21 第164期

作者头像
王很水
发布2024-07-30 15:25:01
1090
发布2024-07-30 15:25:01
举报
文章被收录于专栏:C++ 动态新闻推送

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

本期文章由 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=%s\n", 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) = %f\n", cbrt(x)); // selects the default cbrt
    printf("cbrtf(3.375) = %f\n", 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

大概思路已经有了,怎么实现大家看代码吧

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-07-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 CPP每周推送 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 资讯
  • 文章
    • C++ Error Handling Strategies – Benchmarks and Performance
      • 浅析Cpp 错误处理
        • When __cxa_pure_virtual is just a different flavor of SEGFAULT https://blog.the-pans.com/pure_virtual/
          • What’s the point of std::monostate? You can’t do anything with it!
            • https://devblogs.microsoft.com/oldnewthing/20240708-00/?p=109959
            • 开源项目介绍
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档