写点什么

C++ 动态新闻推送 第 33 期

作者:很水
  • 2021 年 11 月 12 日
  • 本文字数:4560 字

    阅读完需:约 15 分钟

reddit/hackernews/lobsters/摘抄一些 c++动态


这周周末有事,发的比较早


周刊项目地址在线地址知乎专栏 |腾讯云+社区


欢迎投稿,推荐或自荐文章/软件/资源等,请提交 issue



资讯

编译器信息最新动态推荐关注 hellogcc 公众号

OSDT Weekly 2021-10-13 第119期


QT 6出beta版本了

文章


大概内容,rust 并没有比 c++快和安全。唯一优点就是生命周期检查


很多代码场景下 c++的灵活性要高于强制安全检查,且一些场景下 rust 生成的汇编不如 c++少



serenity 是一个 c++写的操作系统,分享了一些开发记录/采访



一篇 CRTP 示例。主要解决的问题,基本接口实现,不需要 virtual



TODO:看不懂讲的啥



用 c 写个 lisp



讲各种各样的 cast


这里着重介绍一下 bit_cast,这个就是强制解释的 memcpy 版本,对于内建基础类型使用的,比如


#include <cstdint>#include <bit>#include <iostream> constexpr double f64v = 19880124.0;constexpr auto u64v = std::bit_cast<std::uint64_t>(f64v); constexpr std::uint64_t u64v2 = 0x3fe9000000000000ull;constexpr auto f64v2 = std::bit_cast<double>(u64v2); int main(){    std::cout        << std::fixed <<f64v << "f64.to_bits() == 0x"        << std::hex << u64v << "u64\n";     std::cout        << "f64::from_bits(0x" << std::hex << u64v2 << "u64) == "        << std::fixed << f64v2 << "f64\n";}
复制代码


实现就是 memcpy 硬拷,其实这种需求用 union 不就搞定了。多个 copy 换安全吗



考虑 benchmark 一段代码


bench_input = 42;start_time = time();bench_output = run_bench(bench_input);result = time() - start_time;
复制代码


这段代码的问题在于,编译器可能会重排 time()导致 run_bench 的时间不准确


要保证,run_bench 必须在两条 time 计算之间,不会被优化/重排 如何做?


google benchmark 已经做过类似的工作DoNotOptimize()


bench_input = 42;start_time = time();DoNotOptimize(bench_input)bench_output = run_bench(bench_input);DoNotOptimize(bench_output)result = time() - start_time;
复制代码


DoNotOptimize()的作用是如何实现的?


inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp& value) {    asm volatile("" : "+r,m"(value) : : "memory");}
复制代码


这个 asm 可能看不懂,根据GNU extended inline asm syntax,是这个意思


asm asm-qualifiers ( AssemblerTemplate                 : OutputOperands                 [ : InputOperands                 [ : Clobbers ] ])
复制代码


针对这行 asm,volatile 暗示会变,让编译器不优化,AssemblerTemplate 是空的,也就是明显是空的无作用的汇编也不要删掉?


"memory" 也就是 "clobbers memory" 明示直接内存读,也就是暗示这个值经常变


output constraints ("+r,m"(value) 明示读写这个 value


是不是必须要用 clobber memory?


有个类似的实现


inline BENCHMARK_ALWAYS_INLINE void EnsureMaterialise(Tp& value) {    asm volatile("" : "+r,m"(value) : :); // Doesn't clobber memory.}
复制代码


也是暗示 value 会变,也是暗示 value 不被优化,但是不能保证 value 的全局副作用,还是会被重排,这个用来测试比如 jit 优化 constant propagation 优化之类的场景,看差异


我们要保证,value 的计算是影响周围的调用的,所以,要标记 value 是可变只能从内存读/寄存器读读(clobber memory)这样就有全局副作用,对于相关的函数调用,能保证不被重排。


所以重新回顾一下上面这段代码


bench_input = 42;
// May have global side-effects.start_time = time();
// Also may have global side-effects.// Needs to observe any side-effects of `time()`, so can't be re-ordered before it.// Forces `bench_input` to be materialized, despite it being a constant.DoNotOptimize(bench_input)// Here the compiler must assume that `bench_input` has now been mutated.
// Is expected to observe the potentially mutated value of `bench_input`, therefore// cannot be reordered before `DoNotOptimize()`.bench_output = run_bench(bench_input);
// May have global side-effects.// Depends on `bench_output` so cannot be reordered above `run_bench()`.DoNotOptimize(bench_output)
// Also may have global side-effects.// Needs to observe any potential side effects of `DoNotOptimize(bench_output)`, so// cannot be reordered before it.result = time() - start_time;
复制代码


视频

Core C++ 2021

有一些不是英语还没有字幕,实在看不懂,跳过



介绍了协程的几个猥琐用法


比如用于树的遍历,协程的栈比函数栈要省



Clang/gcc 用-Rpass 可以看到优化的具体细节,不方便看?想要其他细节?借助工具,opt-viewer 就是这么个工具,llvm 组件里带的


但是有一定的缺点,CPU 占用/内存之类的,作者改了一个optview2


并展示了一些用法示例,这个工具对于编译器分析有点帮助。



介绍了参数传递对性能的影响,列了一些极端场景。这里贴例子


传值比传引用重?传引用比传值轻?一般来说是,也有反例


STL 中的场景


拷贝不可避免,比如 accumulate,也更安全,比如 transform


下面是几个好玩的例子(坑爹的用法)


const T&不一定是不可变的


void scale_down(vector<double>& v, const double& a) {  for(auto&i : v) i /= a;}std::vector<double> a1{2, 2, 2};scale_douw(a1,a1[0]);// 1 2 2
复制代码


我感觉这代码不喝两瓶啤酒写不出来


但是这种代码是有可能写出来的


#include <vector>#include <iostream>#include <iterator>#include <string>#include <sstream>#include <cstdio>using namespace std;
void inline print_vector(const std::vector<int> & v){ ostringstream oss; copy(v.begin(), v.end(), std::ostream_iterator<int>(oss, " ")); printf("%s\n", oss.str().c_str());}int main(){ vector<int> vec {1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6}; vec.erase(std::remove( begin(vec), end(vec), *std::max_element(begin(vec), end(vec))), end(vec)); print_vector(vec); // 1 2 3 4 5 1 2 3 4 5 6 2 3 4 5 6 }
复制代码


引用的位置对应的值变了,第一组 1 2 3 4 5 6 删掉了 6,第二组的 1 补位,逻辑变成 remove 1,然后 2 3 4 5 6 没删除,然后第三组 1 2 3 4 5 6,找到了 1,最终就是这个效果


只能说,写代码的的时候少喝点酒


如何解决这个问题?把指针转换成值,强转一下,去掉指针信息,或者用 decay_copy,原理都是一样的


template <class T>  typename std::decay<T>::type    decay_copy (T&& t) {    return std::forward<T>(t);  }
复制代码


传引用反而比传值慢 godbolt


计算,传引用,寄存器利用效率不高,性能差, 用不上向量化


void byRef(std::vector<double>& v, const double& coeff) {  for (auto& i : v) i *= std::sinh(coeff);}
void byVal(std::vector<double>& v, double coeff) { for (auto& i : v) i *= std::sinh(coeff);}
复制代码


其实这背后有个问题,就是指针暗示着可能改动,所以不能尽可能 的优化,所以 c 中有 restrict 关键字,告诉你,这个指针在这个范围内不会被改,让编译器大胆做优化


作者还介绍了 herb 的一些实践,使用 concept 约束参数,以及思考 std::ref stdx::val 的用法等等。不过上面的代码例子是比较有意思值的看的了



python 实现 RAII


class Greeter:  def __init__(self, name):    self.name = name    print(f"hello, {self.name}!")      def __enter__(self):    return self
def __exit__(self, e_type, e_val, e_tb): print(f"goodbye, {self.name}!")
def main(): with Greeter(1): print("we have a greeter")
main()
复制代码


有了 RAII,一个 scopeguard 就有了


class DtorScope:  def __init__(self):    self.stack = []
def __enter__(self): return self def __exit__(self, e_type, e_val, e_tb): while self.stack: self.stack.pop().__exit__(e_type, e_val, e_tb) def push(self, cm): self.stack.append(cm)
复制代码


然后可以结合闭包,装饰器模式


def cpp_function(f):  def _wrapper(*args, **kwargs):    with DtorScope():      return f(*args, **kwargs)    return _wrapper
复制代码


这样就直接装饰 main 就行了


main = cpp_function(main)main()
复制代码


或者直接


@cpp_functiondef main():  ...
复制代码


等一下,我们是 c++周报,后面不展开了


PPT 在这里,代码在这里


项目

  • weggli rust 写的一个程序,能够搜索代码中的代码块。不是简单的关键字搜索,是模式搜索


看官方的例子



虽然是 rust 写的,但是是 c++代码分析工具,所以放在这里了


TODO:有没有可能用 c++重写?


  • Discontinue Sourcetrail sourcetrail 是一个 c++写的代码浏览工具,类似 source insight,团队放弃开发了。不过现在这个领域没有人能搞的过 jetbrains,开源用爱发电+社区推动确实疲惫。祝好

  • laugh Light Actor Framework一个轻量的 actor 实现

  • tkrzw 一个数据库实现,重写 Tokyo Cabinet,文档在这里 也有个 dbserver文档 其实之前我还真有过重写 tokyocabinet 的点子。不过搁置了

  • ttauri 一个 c++20 GUI 库

  • CrowCpp 是一个 c++ http 库框架,0.3 版本发布




看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持!


本文永久链接

发布于: 1 小时前阅读数: 4
用户头像

很水

关注

还未添加个人签名 2020.10.21 加入

还未添加个人简介

评论

发布
暂无评论
C++ 动态新闻推送 第33期