【问题标题】:Is there any practical considerations to prefer one notation for converting vectors into slices?是否有任何实际考虑来选择一种将向量转换为切片的表示法?
【发布时间】:2017-02-15 05:17:57
【问题描述】:

可以通过以下任一方式将向量取消引用到切片中:

  • let slice = &*my_vec;
  • let slice = &my_vec[..];

我更喜欢第二种,尽管它更冗长,但我发现它更清楚,尤其是当语句与密集使用的运算符混合时,并且根据Box/Vec/指针类型,取消引用具有不同的含义.

另一方面,它使用了一个冗余范围。

我想忽略代码风格的个人偏好,而专注于有形的差异。他们是否曾经为发布版本编译成不同的代码?

【问题讨论】:

  • 如果需要切片类型(例如在函数参数中或返回值中),也只有 &my_vec

标签: rust slice notation


【解决方案1】:

优化后完全没有区别:

#[no_mangle]
extern {
    fn simple(ptr: *const u8, len: usize) -> usize;
}

fn take_slice(slice: &[u8]) {
    unsafe { simple(slice.as_ptr(), slice.len()); }
}

#[inline(never)]
fn take_vec_auto(v: &Vec<u8>) {
    take_slice(v);
}

#[inline(never)]
fn take_vec_deref(v: &Vec<u8>) {
    take_slice(&*v);
}

#[inline(never)]
fn take_vec_index(v: &Vec<u8>) {
    take_slice(&v[..]);
}

导致以下 LLVM IR on the playground:

; Function Attrs: noinline nounwind uwtable
define internal fastcc void @_ZN8rust_out13take_vec_auto17h2827abd8ce79beacE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
entry-block:
  %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
  ret void
}

; Function Attrs: noinline nounwind uwtable
define internal fastcc void @_ZN8rust_out14take_vec_deref17h66cf4ce954b36d1dE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
entry-block:
  %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
  ret void
}

; Function Attrs: noinline nounwind uwtable
define internal fastcc void @_ZN8rust_out14take_vec_index17h77571b14bbdb120cE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
entry-block:
  %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
  ret void
}

所以主要是风格问题,风格是主观的。

【讨论】:

  • 这是正确答案。一个人不应该担心这样的小事。如今,优化器非常聪明。这是一件好事,因为它承担了我们为每一行代码考虑性能的负担。首先测量和识别问题;你仍然可以担心微优化:)
【解决方案2】:

据我所知,基于当前的夜间发布模式 MIR,第一个变体更可取,因为它减少了一次分配:

let mut _0: ();
scope 1 {
    let _1: std::vec::Vec<i32>;
    scope 2 {
        let _6: &[i32];
    }
}
let mut _2: ();
let mut _3: std::boxed::Box<[i32]>;
let mut _4: std::boxed::Box<[i32; 3]>;
let mut _5: std::boxed::Box<[i32; 3]>;
let mut _7: &[i32];
let mut _8: &std::vec::Vec<i32>;
let mut _9: std::ops::RangeFull; // not present in variant 1

但是,我不知道进一步优化后会是什么样子 - 根据目标使用情况可能会有所不同。

【讨论】:

  • 我认为我们不应该关注 MIR,因为它几乎不执行优化。我希望两个变体在 LLVM 通过后编译为同一个程序集
  • 这是最可能的结果,但是用 LLVM IR / ASM 验证这一点是不可行的,因为没有实际输出 everything 将被优化掉(尤其是在发布模式下) .
  • RangeFull 的大小为零,所以我认为额外的分配没有那么重要。
  • 在 LLVM IR 级别上没有区别(优化后)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 2020-09-27
  • 1970-01-01
相关资源
最近更新 更多