performance - 为什么 if 语句正在提高性能无用?
问题描述
我正在用 rust 编写一个小型库作为“碰撞检测”模块。在尝试尽可能快地进行线段 - 线段相交测试时,我遇到了一个奇怪的差异,其中看似无用的代码使函数运行得更快。
我的目标是尽可能优化这种方法,并且知道为什么可执行代码会像我想象的那样显着改变性能,这将有助于实现该目标。无论是 rust 编译器中的一个怪癖还是其他什么,如果有人知识渊博能够理解这一点,这就是我到目前为止所拥有的:
两个功能:
fn line_line(a1x: f64, a1y: f64, a2x: f64, a2y: f64, b1x: f64, b1y: f64, b2x: f64, b2y: f64) -> bool {
let dax = a2x - a1x;
let day = a2y - a1y;
let dbx = b2x - b1x;
let dby = b2y - b1y;
let dot = dax * dby - dbx * day;
let dd = dot * dot;
let nd1x = a1x - b1x;
let nd1y = a1y - b1y;
let t = (dax * nd1y - day * nd1x) * dot;
let u = (dbx * nd1y - dby * nd1x) * dot;
u >= 0.0 && u <= dd && t >= 0.0 && t <= dd
}
fn line_line_if(a1x: f64, a1y: f64, a2x: f64, a2y: f64, b1x: f64, b1y: f64, b2x: f64, b2y: f64) -> bool {
let dax = a2x - a1x;
let day = a2y - a1y;
let dbx = b2x - b1x;
let dby = b2y - b1y;
let dot = dax * dby - dbx * day;
if dot == 0.0 { return false } // useless branch if dot isn't 0 ?
let dd = dot * dot;
let nd1x = a1x - b1x;
let nd1y = a1y - b1y;
let t = (dax * nd1y - day * nd1x) * dot;
let u = (dbx * nd1y - dby * nd1x) * dot;
u >= 0.0 && u <= dd && t >= 0.0 && t <= dd
}
Criterion-rs 基准代码:
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("line-line", |b| b.iter(|| line_line(
black_box(0.0), black_box(1.0), black_box(2.0), black_box(0.0),
black_box(1.0), black_box(0.0), black_box(2.0), black_box(4.0))));
c.bench_function("line-line-if", |b| b.iter(|| line_line_if(
black_box(0.0), black_box(1.0), black_box(2.0), black_box(0.0),
black_box(1.0), black_box(0.0), black_box(2.0), black_box(4.0))));
}
请注意,使用这些输入,if 语句将不会评估为真。
assert_eq!(line_line_if(0.0, 1.0, 2.0, 0.0, 1.0, 0.0, 2.0, 4.0), true); // does not panic
Criterion-rs 基准测试结果:
line-line time: [5.6718 ns 5.7203 ns 5.7640 ns]
line-line-if time: [5.1215 ns 5.1791 ns 5.2312 ns]
Godbolt.org 对这两段代码的 rust 编译结果。
不幸的是,我无法像我想要的那样阅读 asm。我不知道如何让 rustc 编译它而不在网站内执行整个计算的内联,但如果你能做到这一点,我希望这可能会有所帮助?
我当前的环境是: -Windows
10(最新标准)
-Ryzen 3200G cpu-
标准 Rust 安装(无夜间/测试版),使用 znver1 编译(最后我检查过)
正在使用cargo bench
命令进行测试,我认为它正在进行优化,但在任何地方都没有找到对此的确认。Criterion-rs 正在做所有的基准测试。
如果您能够回答这个问题,请提前感谢您。
编辑:
将godbolt链接中的代码与-C opt-level=3 --target x86_64-pc-windows-msvc
(我当前安装的rustc使用的工具链)进行比较,可以发现编译器在函数中插入了许多unpcklpd和unpckhpd指令,这可能会减慢代码速度而不是优化它。将 opt-level 参数设置为 2 会删除额外的 unpck 指令,而不会显着改变编译结果。但是,添加
[profile.release]
opt-level = 2
虽然导致重新编译,但向后性能保持不变,与opt-level = 1
. 我没有target-cpu=native
指定。
解决方案
推荐阅读
- android - 是否有任何 Android 通知 setContentTitle 和 setContextText 魔法值?
- java - 如何使用具有使用 java 流的属性的不同对象的列表来创建不同对象的列表
- reactjs - 我想对 usestate 或其他钩子做出反应。实施 60 秒倒计时。如何
- php - LARAVEL 调用未定义的方法 Illuminate\Database\Eloquent\Builder::splice()
- java - 我想要图像,然后是文本视图,然后是声音.. android studio
- node.js - 猫鼬更新时没有报错,但是不更新
- docker - 使用 docker 和 nginx 在 5000 端口部署静态网站
- c# - 获取 JSON 中指定的错误解析类型
- flutter - Flutter 错误:无法加载我的音频资产
- java - 滚动整个片段,里面有片段