从文档中可以看出:
Run Code Online (Sandbox Code Playgroud)pub fn new(x: T) -> Box<T>在堆上分配内存,然后放入
x其中。
但“地点”是一个棘手的词。如果我们写
let arr_boxed = Box::new([0;1000]);
Run Code Online (Sandbox Code Playgroud)
会[0;1000]在堆上就地初始化吗?
如果我们写
let arr = [0;1000];
let arr_boxed = Box::new(arr);
Run Code Online (Sandbox Code Playgroud)
[0;1000]编译器是否足够聪明,可以首先在堆上初始化?
Chr*_*isB 22
Box::new() 会从堆栈复制到堆吗?
有时。Rust 语言不保证这种优化的发生,并且似乎将其留给 LLVM 来解决。因此,如果先初始化数组然后传递它根本没有关系,因为这对于后端来说本质上是相同的。
在实践中,性能将取决于具体情况。您给出的示例实际上很特殊,因为数据全为零:
pub fn foo() -> Box<[i32; 1000]> {
return Box::new([0; 1000]);
}
Run Code Online (Sandbox Code Playgroud)
在我的测试中,编译器能够将其转换为分配+对memset堆数据的调用。
注意:仅在打开优化的情况下。在调试模式下它将复制。
另一方面,您可能希望使用已知值初始化数据:
pub fn bar(v: i32) -> Box<[i32; 1000]> {
return Box::new([v; 1000]);
}
Run Code Online (Sandbox Code Playgroud)
令我恐惧的是,编译器决定初始化堆栈上的整个数据,然后调用memcpy. (至少它展开了填充循环):)。即使对于像这样的非常大的数据也会发生这种情况[v; 100000],这会因堆栈溢出而使您的程序崩溃。使用编译时已知(非零)文字的[64; 100000]行为方式相同。
如果你真的想确定,你可以这样做:
pub fn baz(v: i32) -> Box<[i32; 1000]>{
unsafe {
let b = std::alloc::alloc(
std::alloc::Layout::array::<i32>(1000).unwrap_unchecked()
) as *mut i32;
for i in 0..1000 {
*b.add(i) = v;
}
Box::from_raw(b as *mut [i32; 1000])
}
}
Run Code Online (Sandbox Code Playgroud)
这是正确的做法。
一个安全版本baz是:
pub fn baz(v: i32) -> Box<[i32; 1000]>{
unsafe {
let b = std::alloc::alloc(
std::alloc::Layout::array::<i32>(1000).unwrap_unchecked()
) as *mut i32;
for i in 0..1000 {
*b.add(i) = v;
}
Box::from_raw(b as *mut [i32; 1000])
}
}
Run Code Online (Sandbox Code Playgroud)
编译器对其进行了很好的优化,基本上与baz.
甚至更短
vec![v; 1000].into_boxed_slice().try_into::<Box<[i32; 1000]>>().unwrap()
这可能是最好的版本。
| 归档时间: |
|
| 查看次数: |
1908 次 |
| 最近记录: |