不重新分配而将Vec转换为FFI的正确方法是什么? [英] What is the correct way to convert a Vec for FFI without reallocation?

查看:58
本文介绍了不重新分配而将Vec转换为FFI的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在FFI中传递元素的 Vec .做实验时,我遇到了一些有趣的观点.我首先给FFI全部3个: ptr len capacity ,以便可以将 Vec 重建为以后销毁它:

I need to pass a Vec of elements across the FFI. Experimenting, I came across a few interesting points. I started with giving the FFI all 3: ptr, len and capacity so that I could reconstruct the Vec to destroy it later:

let ptr = vec.as_mut_ptr();
let len = vec.len();
let cap = vec.capacity();
mem::forget(vec);
extern_fn(ptr, len, cap);

// ...

pub unsafe extern "C" fn free(ptr: *mut u8, len: usize, cap: usize) {
    let _ = Vec::from_raw_parts(ptr, len, cap);
}

我想摆脱 capability ,因为这对我的前端没有用;只是为了我可以重构向量以释放内存.

I wanted to get rid of capacity as it's useless to my frontend; it's just so that I can reconstruct my vector to free the memory.

Vec :: shrink_to_fit()很诱人,因为它似乎消除了处理容量的需要.不幸的是,关于它的文档并不能保证会增加 len ==容量,因此我认为在 from_raw_parts()期间可能会触发未定义行为.

Vec::shrink_to_fit() is tempting as it seems to eliminate the need of dealing with capacity. Unfortunately, the documentation on it does not guarantee that it'll make len == capacity, hence I assume that during from_raw_parts() will likely trigger Undefined Behavior.

into_boxed_slice()似乎可以保证从

into_boxed_slice() seems to have a guarantee that it's going to make len == capacity from the docs, so I used that next. Please correct me if I'm wrong. The problem is that it does not seem to guarantee no-reallocation. Here is a simple program:

fn main() {
    let mut v = Vec::with_capacity(1000);
    v.push(100u8);
    v.push(110);
    let ptr_1 = v.as_mut_ptr();
    let mut boxed_slice = v.into_boxed_slice();
    let ptr_2 = boxed_slice.as_mut_ptr();
    let ptr_3 = Box::into_raw(boxed_slice);
    println!("{:?}. {:?}. {:?}", ptr_1, ptr_2, ptr_3);
}

如果它必须找到新的内存而不是能够在不引起复制的情况下释放额外的容量,那么这是不好的.

This is not good if it has to find new memory instead of being able to shed off extra capacity without causing a copy.

还有其他方法可以将我的向量通过FFI(传递到C)而不传递容量吗?似乎我需要 into_boxed_slice(),但是为什么它涉及重新分配和复制数据?

Is there any other way I can pass my vector across the FFI (to C) and not pass capacity? It seems into_boxed_slice() is what I need, but why does it involve re-allocation and copying data?

推荐答案

原因相对简单.

现代内存分配器将在大小"的slab中隔离分配,其中每个slab负责处理给定的大小范围.例如:

Modern memory allocators will segregate allocations in "sized" slabs, where each slab is responsible for dealing with a given range of sizes. For example:

  • 8个字节的平板:1到8个字节的任何内容
  • 16个字节的平板:从9到16个字节的任何内容
  • 24个字节的平板:从17到24个字节的任意数字
  • ...

分配内存时,您要求给定大小,分配器会找到正确的平板,从中获取一个块,然后返回指针.

When you allocate memory, you ask for a given size, the allocator finds the right slab, gets a chunk from it, and returns your pointer.

当您释放内存时...您如何期望分配器找到正确的平板?有两种解决方案:

When you deallocate memory... how do you expect the allocator to find the right slab? There are 2 solutions:

  • 分配器可以某种方式搜索包含您的内存范围的slab,这涉及通过slab进行线性搜索或某种全局查找表或...
  • 告诉分配器分配的块的大小是多少
  • the allocator has a way to search for the slab that contains your range of memory, somehow, which involves either a linear search through the slabs or some kind of global look-up table or ...
  • you tell the allocator what was the size of the allocated block

在这里很明显,C接口( free realloc )是相当低等的,因此Rust希望使用更高效的接口.责任在于呼叫者.

It's obvious here that the C interface (free, realloc) is rather sub-par, and therefore Rust wishes to use the more efficient interface instead, the one where the onus is on the caller.

因此,您有两种选择:

  1. 通过容量
  2. 确保长度和容量相等

您已经意识到,(2)可能需要 new 分配,这是非常不希望的.(1)可以通过全部传递容量来实现,也可以将其存储在某个位置,然后在需要时进行检索.

As you realized, (2) may require a new allocation, which is quite undesirable. (1) can be implemented either by passing the capacity the whole way, or stash it at some point then retrieve it when you need it.

就是这样.您必须评估您的权衡.

That's it. You have to evaluate your trade-offs.

这篇关于不重新分配而将Vec转换为FFI的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆