在运行时在堆上分配缓冲区

Posted

技术标签:

【中文标题】在运行时在堆上分配缓冲区【英文标题】:Allocating a buffer on the heap at runtime 【发布时间】:2015-12-01 14:05:00 【问题描述】:

我正在通过编写简单的二进制解码器来学习 Rust。

我正在使用 BufferedReader 和 byteorder crate 来读取数字,但我在读取字节缓冲区时遇到了问题。

我想将字节数据读入运行时分配的缓冲区。 然后我想将此缓冲区的所有权传递给一个结构。当 struct 不再使用时,应该释放缓冲区。

除了一些Vec::with_capacity() hacks 之外,似乎没有办法在堆上分配大小在运行时确定的数组。任何想法如何使用适当的 Rust 语义来实现这一点?

【问题讨论】:

您的问题似乎更接近“如何在稳定代码中使用box 的功能”。也许你应该更新你的标题。 只是一点“语义”:box 正确的Rust,它现在还不是稳定的Rust :) 【参考方案1】:

这将创建一个预先分配的可变 500MB 字节缓冲区,存储在堆上,无需不安全的 rust:

// 正确

let mut buffer = vec![0_u8; 536870912];

请注意,下面的代码不是一个好主意,很可能会导致堆栈溢出,因为缓冲区是在装箱并移动到堆之前在堆栈上创建的。

// 不正确 - 使用了堆栈

let mut bytes: Box<[u8]> = Box::new([0_u8; 536870912])

// 不正确 - 慢

let mut bytes = Vec::with_capacity(536870912);
for _ in 0..bytes.capacity() 
    bytes.push(0_u8);

【讨论】:

【参考方案2】:

Rust 是一种低级语言;因此,您可以分配原始内存,然后自己用对象填充它。当然,它需要unsafe 代码,就像所有摆弄原始内存一样。

这里是a complete example:

use std::
    alloc::self, Layout,
    mem, ptr,
;

fn main() 
    unsafe 
        let layout = Layout::from_size_align(512 * 1024, 4 * 1024).expect("Invalid layout");
        let mut raw: *mut i32 = mem::transmute(alloc::alloc(layout));

        for i in 0..(512 * 1024 / 4) 
            ptr::write(raw, i as i32);
            raw = raw.offset(1)
        
    

当然,在实际代码中,我只会使用Vec 为我安全地管理内存。更简单!

【讨论】:

自己用对象填充 — 是否值得一提零大小的类型和析构函数以及解释为什么 Vec 更好用的所有其他复杂性?有没有可能使用as *mut i32 而不是transmute?也许使用mem::size_of 而不是硬编码4?出于我自己的好奇,为什么要将内存对齐到 4K 块?为“只使用Vec”+1 ^_^. @Shepmaster:我在列举大量潜在问题时犹豫不决,但我担心这听起来像漫无边际...... 有一天,你可以成为一个专业的漫步者,比如我自己! ;-)【参考方案3】:

我尝试使用box,但它似乎是实验性的,我不能将它与发布分支一起使用。任何想法如何使用适当的 Rust 语义来实现这一点?

这在 The Rust Programming Language 中有所介绍,特别是“Using Box<T> to Point to Data on the Heap”部分。

使用Box::new:

fn main() 
    let answer: Box<u8> = Box::new(42);

另见:

Allocate array onto heap with size known at runtime Is there any way to allocate a standard Rust array directly on the heap, skipping the stack entirely? How to allocate arrays on the heap in Rust 1.0? Creating a fixed-size array on heap in Rust How do I allocate an array at runtime in Rust? Thread '<main>' has overflowed its stack when allocating a large array using Box

【讨论】:

是的,Box::new 在堆上创建变量。但据我所知,调用Box::new 在堆栈上创建变量,函数调用会将其复制到堆中。 box &lt;expr&gt; 语法应该在堆 AFAIK 上创建。这可能是个问题,因为我想创建 512 KB 的缓冲区。 @semtexzv 你能指出一些证实这一点的文档或反汇编吗? Rust 底层的优化器(由 LLVM 提供)非常强大。 let mut xx = Box::new([0u8;5000000]); 导致堆栈溢出。它应该分配 5 兆的数据。但我可能理解错了。 @semtexzv 如果你 compile in release mode 则不是,这会启用优化。 问题不是使用Box::new问题是在运行时直接在堆上分配一个数组。

以上是关于在运行时在堆上分配缓冲区的主要内容,如果未能解决你的问题,请参考以下文章

在递归函数中在堆上分配与在堆栈上分配

使用类模板时在堆上创建对象

C++ 在堆上分配相同类型的变量会花费截然不同的时间

当由另一个在堆上分配的对象创建时,对象在哪里分配? [复制]

在 32 位 linux 内核上使用 c++ 在堆上分配超过 2GB

我应该啥时候在堆上分配? (C++)