我有一个小结构:
pub struct Foo {
pub a: i32,
pub b: i32,
pub c: i32,
}
我使用的是 (a,b) (b,c) (c,a)
形式的字段对。为了避免代码重复,我创建了一个实用函数,它允许我迭代对:
fn get_foo_ref(&self) -> [(&i32, &i32); 3] {
[(&self.a, &self.b), (&self.b, &self.c), (&self.c, &self.a)]
}
我必须决定是应该将值作为引用返回还是复制 i32
。后来,我打算改用非 Copy
类型而不是 i32
,所以我决定使用引用。我预计生成的代码应该是等效的,因为所有内容都将被内联。
我总体上对优化持乐观态度,因此我怀疑与手写代码示例相比,使用此功能时的代码是等效的。
首先是使用函数的变体:
pub fn testing_ref(f: Foo) -> i32 {
let mut sum = 0;
for i in 0..3 {
let (l, r) = f.get_foo_ref()[i];
sum += *l + *r;
}
sum
}
然后是手写变体:
pub fn testing_direct(f: Foo) -> i32 {
let mut sum = 0;
sum += f.a + f.b;
sum += f.b + f.c;
sum += f.c + f.a;
sum
}
令我失望的是,所有 3 种方法都产生了不同的汇编代码。最差的代码是针对有引用的情况生成的,而最好的代码是根本没有使用我的实用程序函数的代码。这是为什么?在这种情况下,编译器不应该生成等效代码吗?
您可以查看 resulting assembly code on Godbolt ;我也有“等效”assembly code from C++ .
在 C++ 中,编译器在 get_foo
和 get_foo_ref
之间生成等效代码,尽管我不明白为什么所有 3 种情况的代码都不等效。
为什么编译器没有为所有 3 种情况生成等效代码?
更新:
我稍微修改了代码以使用数组并添加了一个直接案例。
Rust version with f64 and arrays
C++ version with f64 and arrays
这次在C++中生成的代码完全一样。但是 Rust 的程序集不同,通过引用返回会导致更糟糕的程序集。
好吧,我想这是另一个例子,没有什么是理所当然的。
最佳答案
TL;DR:微基准测试是骗人的,指令数不会直接转化为高/低性能。
Later on, I plan to switch to a non-Copy type instead of an i32, so I decided to use references.
然后,您应该检查为您的新类型生成的程序集。
在您的优化示例中,编译器非常狡猾:
pub fn testing_direct(f: Foo) -> i32 { let mut sum = 0; sum += f.a + f.b; sum += f.b + f.c; sum += f.c + f.a; sum }
产量:
example::testing_direct: push rbp mov rbp, rsp mov eax, dword ptr [rdi + 4] add eax, dword ptr [rdi] add eax, dword ptr [rdi + 8] add eax, eax pop rbp ret
大致是 sum += f.a;总和 += f.b;总和 += f.c; sum += sum;
.
也就是说,编译器意识到:
f.X
被添加了两次f.X * 2
相当于加了两次
虽然前者在其他情况下可以通过使用间接来抑制,但后者非常特定于 i32
(并且加法是可交换的)。
例如,将您的代码切换到 f32
(仍然是 Copy
,但加法不再是可交换的),我为 testing_direct< 得到了完全相同的程序集
和 testing
(testing_ref
略有不同):
example::testing: push rbp mov rbp, rsp movss xmm1, dword ptr [rdi] movss xmm2, dword ptr [rdi + 4] movss xmm0, dword ptr [rdi + 8] movaps xmm3, xmm1 addss xmm3, xmm2 xorps xmm4, xmm4 addss xmm4, xmm3 addss xmm2, xmm0 addss xmm2, xmm4 addss xmm0, xmm1 addss xmm0, xmm2 pop rbp ret
再也没有诡计了。
所以从你的例子中真的不可能推断出太多,检查真实类型。
关于rust - 为什么生成的程序集在内联时通过引用返回和复制不等价?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42076205/