【Rust日报】 2019-08-14:在Facebook上反复出现的 C++ bug

VecOption: 与Vec<Option>等价但更高效的库

#StdExtend

Repo: https://github.com/sivadeilra/vec_option

Syn 和 Quote 1.0 发布

#macro #syn

Syn 和 Quote 此次 1.0 稳定,意味着API接口稳定,但是,并不代表着Rust的语法树稳定。Syn和Quote内部还是会随着Rust的变化而改动,只不过不会影响 Syn 和 Quote 的 API稳定。

注意:Syn和Quote的1.0版本最低依赖Rust 1.31版本。发布日志里还记录了一些Break change,需要注意。

Read More: https://github.com/dtolnay/syn/releases/tag/1.0.0

cargo-docset: 可以生成支持Dash和Zeal的文档集

#docs

Dash和Zeal都是著名的编程语言文档集工具

Repo: https://github.com/Robzz/cargo-docset

async-stream: 提供了stream!宏方便编写异步流

#async #stream #tokio

Repo: https://github.com/tokio-rs/async-stream

「SO问答」对超过240个元素的数组进行循环时,为什么会有很大的性能影响?

#stackoverflow

问题:

下面代码当 CAPACITY >= 240 的时候,与 CAPACITY >= 239 相比,性能慢了80倍。Rust编译器专门为240以内的长度做了优化?使用 rustc -C opt-level=3 进行编译。

use std::time::Instant;

const CAPACITY: usize = 240;
const IN_LOOPS: usize = 500000;

fn main() {
    let mut arr = [0; CAPACITY];
    for i in 0..CAPACITY {
        arr[i] = i;
    }
    let mut sum = 0;
    let now = Instant::now();
    for _ in 0..IN_LOOPS {
        let mut s = 0;
        for i in 0..arr.len() {
            s += arr[i];
        }
        sum += s;
    }
    println!("sum:{} time:{:?}", sum, now.elapsed());
}

解答:

总结:低于240,LLVM完全展开内部循环,可以优化掉重复循环,增加性能。

分析:

这是一个神奇的阈值,超过该阈值LLVM将停止执行某些优化。阈值是 8字节* 240 = 1920字节(数组是usizes数组,因此长度乘以8字节,假设 x86-64 CPU)。在该问题中的基准测试中,是仅针对长度239执行的一个特定优化,所以导致了巨大的性能差异。

比如这段代码:

pub fn foo() -> usize {
    let arr = [0; 240];
    let mut s = 0;
    for i in 0..arr.len() {
        s += arr[i];
    }
    s
}

你在 godbolt 编辑器中查看生成的汇编代码,比较240和239,会发现有很大区别。比如当239的时候生成:

movdqa  xmm1, xmmword ptr [rsp + 32]
movdqa  xmm0, xmmword ptr [rsp + 48]
paddq   xmm1, xmmword ptr [rsp]
paddq   xmm0, xmmword ptr [rsp + 16]
paddq   xmm1, xmmword ptr [rsp + 64]
; more stuff omitted here ...
paddq   xmm0, xmmword ptr [rsp + 1840]
paddq   xmm1, xmmword ptr [rsp + 1856]
paddq   xmm0, xmmword ptr [rsp + 1872]
paddq   xmm0, xmm1
pshufd  xmm1, xmm0, 78
paddq   xmm1, xmm0

就是所谓的循环展开: LLVM将循环体粘贴一段时间,以避免执行那些“循环管理指令”,即循环变量的增量,检查循环是否结束和跳转。(可以自行对比一下240的输出)。但是,即便循环不展开,也不会造成80倍的性能差异。所以,实际上那个性能测试代码嵌套循环导致的(LLVM生成的代码基本上首先只执行内部循环(计算总和),然后通过多次累加总和来模拟外部循环!)。最好要使用Rust的惯用法: arr.iter().sum(),这样就不会产生80倍的性能差异了。

Read More: https://stackoverflow.com/questions/57458460/why-is-there-a-large-performance-impact-when-looping-over-an-array-over-240-elem

Rust 1.37 预发布测试

#Stable

RUSTUP_DIST_SERVER=https://dev-static.rust-lang.org rustup update stable

新版本中有一些新特性:

  • cfgcfg_attr 中可以用泛型参数了
  • 可以对枚举值使用类型别名了
type MyOption = Option<u8>;

fn increment_or_zero(x: MyOption) -> u8 {
    match x {
        MyOption::Some(y) => y + 1,
        MyOption::None => 0,
    }
}
  • 可以用_来定义常量:const _: u32 = 5;
  • Rust 2015 edition 的宏现在支持 ?语法
  • mem::MaybeUninit和T已经实现ABI兼容,MaybeUninit共享T的大小、对齐方式和ABI。

1.37 Release Notes: https://github.com/rust-lang/rust/blob/stable/RELEASES.md#version-1370-2019-08-15

Reddit讨论:CppCon 2017 - 在Facebook上反复出现的 C++ bug

#Facebook

该贴主提到,他看了Facebook工程总监在CppCon 2017的分享,其中谈到Facebook中经常出现的Bug,他认为,这些Bug是用Safe Rust完全不会写出来的Bug。

以下是这些bug的概述:

  • Bug #1: 越界访问。C++的std::vector的索引运算符不进行边界检查。演讲者称之为“可能是每个代码库中问题的最大原因”。Rust's Vec总是进行边界检查,除非您使用Unsafe。
  • Bug #2: 如果你搜索的关键字不在map中,那么std::map的索引运算符将创建一个默认元素。真奇怪。Facebook发生了两起重大事故,Map显示了一些设置,打印设置时偷偷插入了值为0的新设置。Rust的哈希映射不可能做到这一点。要在Rust中获得这样的行为,您必须使用entry() API对其进行显式编程。
  • Bug #3: 试图避免不必要的复制通常会导致对已经不存在的临时成员的引用(悬垂指针)。C++没有借用检查器来检测这一点。Rust会。
  • Bug 4: volatile。它不会使代码线程安全,但是人们还是这样使用它。Safe Rust根本没有volatile。
  • Bug 5: std::shared_ptr线程安全吗?是像Rc还是像Arc?嗯,这很复杂。它很像Arc,但是如果你实际上在多线程环境中使用它,你仍然有可能出错。Rust既有rc又有Arc,它会阻止你将Rc发送到不同的线程。
  • 赠送的Bug : 人们经常解引用std::shared_ptr,并在不保留std::shared_ptr的情况下对结果进行引用。Rust的借用检查在这里拦住你。
  • Bug #6: 由于C++语法中的一个怪癖,很容易编写看起来像std::mutex的代码,但是实际上它正在创建一个与std::mutex同名的std::unique_lock,隐藏它但不锁定它。这里真正的问题是,在C++中,std::mutex没有连接到它所保护的数据,而在Rust中,如果不锁定它,就根本不可能访问受Mutex<T>保护的数据。
  • 附送的Bug : 在C++中,很容易意外地对事物进行深度复制(Clone)。演讲者和听众中的一个人理所当然地指出,这真的没什么大不了的,事实上,许多bug(见bug #3)都是通过避免不必要的拷贝而引入的。尽管如此,Rust在这里对你也有帮助,因为如果你想克隆一些东西,你通常需要显式地做。
  • 附送的又一个bug:“我们有很多与异步编程相关的生命周期问题,”演讲者说。他称之为“非常关键”和“最重要的缺陷之一”。如果说Rust擅长什么,那就是“与异步编程相关的生命周期问题”。

演讲中从未提到Rust,但如果里面提到Rust的话,该演讲就是Rust最好的广告了 :D

(Libra 选择 Rust,某种意义上,可能也是苦C++久矣)

  • Read More: https://www.reddit.com/r/rust/comments/cq9rco/cppcon_2017_curiously_recurring_c_bugs_at_facebook/
  • CppCon 2017 视频 :https://www.youtube.com/watch?v=lkgszkPnV8g

tnef: 一个纯Rust的 TNEF 解析器

#tnef

TNEF 以 application/ms-tnef 类型的 MIME 附件的形式出现在邮件中。

Repo: https://github.com/newpavlov/tnef

「非官方」Google Play市场的 Crates.io 安卓App 已经更新到了1.5版本

#android

头一次知道还有这个App

Read More: https://play.google.com/store/apps/details?id=com.bmco.cratesiounofficial&hl=en_us

Rust异步编程尝试:GitHub star计数工具

#async

供学习使用

Repo: https://github.com/Byron/github-star-counter

Jilu: 根据Git仓库的状态生成改变日志

#git

自动生成 CHANGELOG.md

Repo: https://github.com/rustic-games/jilu

tokio-i3ipc更新到了async/await

#tokio

tokio-i3ipc的作者写了篇文章记录了此事,也算是一个升级参考。

  • Read More: https://leshow.github.io/post/async_await/
  • Repo: https://github.com/leshow/tokio-i3ipc/

static-assertions-rs 发布 0.3.4 版本

#assert

该库可以实现编译时断言。

Repo: https://github.com/nvzqz/static-assertions-rs


From 日报小组 Chaos

日报订阅地址:

独立日报订阅地址:

  • Telgram Channel
  • 阿里云语雀订阅
  • Steemit
  • GitHub

社区学习交流平台订阅:

  • Rust.cc 论坛: 支持 rss
  • Rust Force: 支持 rss
  • 微信公众号:Rust 语言学习交流

原文发布于微信公众号 - Rust语言学习交流(rust-china)

原文发表时间:2019-08-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券