如何将任意长度的 Vec<T> 的所有条目加载到堆栈中?

How can I load all entries of a Vec<T> of arbitrary length onto the stack?

提问人:Bots Fab 提问时间:2/2/2021 最后编辑:trincotBots Fab 更新时间:6/13/2021 访问量:216

问:

我目前正在使用向量,并试图确保我在堆栈上拥有本质上是我的向量数组的内容。我无法调用,因为我在我的 .这可能吗?Vec::into_boxed_sliceVec

在阅读了关于如何实现 Vec 的 Rustonomicon 之后,它似乎跨越了堆上的指针,在每个条目处取消引用。我想将堆中的条目分块到堆栈中以便快速访问。Vec

向量 rust 堆内存 堆栈分配

评论

1赞 Caesar 2/2/2021
你可能误解了Rustonomicon的某些部分,因为s是记忆的连续分配。当然,元素本身可能在某种程度上是框或指针。无论哪种方式,将您在 Rustonomicon 上阅读的内容联系起来都应该是一个好主意,并显示您期望什么样的 s,所以也许答案可以消除您的困惑。VecT
0赞 Bots Fab 2/2/2021
doc.rust-lang.org/nomicon/vec.html doc.rust-lang.org/nomicon/vec-layout.html 来自 doc.rust-lang.org/std/vec/struct.Vec.html“..Vec 永远不会执行“小优化”,其中元素实际上存储在堆栈上,原因有两个:..“ 因此,我相信所有值都存储在堆上。鉴于 'nomicon 中的迭代实现,我相信它必须在堆上上述的每个指针增量处取消引用。我希望通用 T 是特定的
2赞 Shepmaster 2/2/2021
Vec 的数据始终是连续的(“连续的可增长数组类型”),这在局部性方面是尽可能好的。您似乎混淆了数据类型“堆栈”和“堆”以及内存区域“堆栈”和“堆”。我鼓励你研究其中的区别;我认为这将把事情弄清楚。
1赞 trent 2/2/2021
像往常一样,Shepmaster 是正确的。内存分配的类似堆的性质仅在分配或释放内存时才起作用 - 您不必每次使用存储在堆中的内容时都遍历堆(数据结构)(动态分配内存的通用术语,它可能被构造为实际,也可能不是)。一旦你有了指向数据的指针(a是),无论它是在堆栈上还是在堆上,都无关紧要。Vec
1赞 user4815162342 2/2/2021
没有“任意地址集”。 包含指向数据的简单指针,仅此而已。没有解析,只是一个取消引用,与堆栈上的(任意大小的)数据完全相同。Vec

答:

5赞 Shepmaster 2/2/2021 #1

您可以在 nightly Rust 中使用该功能:unsized_locals

#![feature(unsized_locals)]

fn example<T>(v: Vec<T>) {
    let s: [T] = *v.into_boxed_slice();
    dbg!(std::mem::size_of_val(&s));
}

fn main() {
    let x = vec![42; 100];
    example(x); // Prints 400
}

另请参阅:


我无法调用,因为我在我的Vec::into_boxed_sliceVec

当然可以。

Vec[...] 似乎跨过堆上的指针,在每个条目处取消引用

访问 a 中的每个成员都需要内存取消引用。访问数组中的每个成员都需要内存取消引用。这里的速度没有实质性的区别。Vec

用于快速访问

我怀疑这不会比直接访问中的数据更快。事实上,如果它更慢,我不会感到惊讶,因为你正在复制它。Vec

评论

0赞 Bots Fab 2/2/2021
在我的印象中,从堆栈中取消引用会比堆的数据结构更快。我正在懒惰地实现一种算法,该算法在处理时有时间异步在内存中分块,所以认为这可能会节省一些时间。谢谢,我无法让编译器允许我.into_boxed_slice,因为 [Cell<T>] 没有实现 Sized 特征。我认为这是由于由于 T 已调整大小后没有明确声明 [Cell<T>; n]。我会再试一次,每晚阅读。
1赞 Masklinn 2/2/2021
@BotsFab初始 deref 会更慢,因为堆栈在缓存中,而堆位置不在缓存中,但加载 vec 会以任何一种方式缓存它。除非通过在堆栈上加载缓存,否则您会使缓存膨胀,并且可能使代码本身膨胀(不确定未调整大小的局部变量,但我知道在 C VLA 中会导致绝对垃圾代码生成)。
0赞 Bots Fab 2/2/2021
@Masklinn VLA 相对较小,并且非常适合缓存。它是一种树状结构,其中来自给定节点的边缘被分块到堆栈上,我计划在配置中限制边缘,或者在必要时自动适应缓存行)。到目前为止,这是一个边缘情况,它可能偏离了主题,但似乎正在引起合理的兴趣。这个细节很重要,因为它是一种可扩展的 ML 算法(以及我对速度的情感价值)。
1赞 Masklinn 2/2/2021
@BotsFab如果它们适合缓存,它们将与它们已经拥有的 Vec 一样适合,这充其量应该没有区别。动态堆栈分配的问题在于编译器生成的代码低于标准。
1赞 Masklinn 2/2/2021
TBF 我不知道这是否与 VLA 有相同的问题,但我确实知道 VLA 最终被剥离到 linux 内核(以及各种其他代码库)之外的原因之一是它们会导致非常糟糕的代码。