前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 动态新闻推送 第33期

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

作者头像
王很水
发布2021-10-18 10:49:09
5030
发布2021-10-18 10:49:09
举报

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

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版本,对于内建基础类型使用的,比如

代码语言:javascript
复制
#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一段代码

代码语言:javascript
复制
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()

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

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

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

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

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

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

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

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

是不是必须要用clobber memory?

有个类似的实现

代码语言:javascript
复制
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)这样就有全局副作用,对于相关的函数调用,能保证不被重排。

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

代码语言:javascript
复制
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&不一定是不可变的

代码语言:javascript
复制
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

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

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

代码语言:javascript
复制
#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,原理都是一样的

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

传引用反而比传值慢 godbolt

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

代码语言:javascript
复制
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

代码语言:javascript
复制
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就有了

代码语言:javascript
复制
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)

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

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

这样就直接装饰main就行了

代码语言:javascript
复制
main = cpp_function(main)
main()

或者直接

代码语言:javascript
复制
@cpp_function
def 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版本发布

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

本文永久链接

代码语言:txt
复制
     This site is open source. [Improve this page](https://github.com/wanghenshui/cppweeklynews/edit/dev/posts/033.md).
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C++ 动态新闻推送 第33期
    • 资讯
      • 编译器信息最新动态推荐关注hellogcc公众号
    • 文章
      • 视频
        • Core C++ 2021
      • 项目
      相关产品与服务
      腾讯云代码分析
      腾讯云代码分析(内部代号CodeDog)是集众多代码分析工具的云原生、分布式、高性能的代码综合分析跟踪管理平台,其主要功能是持续跟踪分析代码,观测项目代码质量,支撑团队传承代码文化。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档