在 Rust 中分配/释放内存编译为 WebAssembly

问题描述 投票:0回答:1

我的情况
我一直在尝试使用 Rust 中的部分客户端制作 IO 游戏,使用 wasm-pack 编译为 WebAssembly,我通过 Javascript 访问它。 Rust 在这里用作库,并在需要处理包和游戏逻辑时被调用。为了能够处理逻辑,它需要游戏中所有实体和地图的上下文,这可能是大量数据。现在,需要游戏上下文的函数每秒被调用数百次,我不想实现任何批处理逻辑,只想完全避免该问题。这就是为什么我决定使用 WebAssembly 中的内存系统(https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Memory)来存储游戏状态,以便我可以轻松地从 Rust 检索它。

TLDR:我想避免将游戏上下文(我正在制作 IO 游戏,使用 JS 和 Rust 作为 WASM,使用 wasm-bindgen 和 wasm-pack)从 Javascript 传递到 Rust,而是希望将其存储在内存中。

实际问题
我现在如何分配和释放、读取和写入该内存?我当前的实现(如下)

  • 分配:Vec 与 Vec::with_capacity
  • 释放:std::slice::from_raw_parts_mut(ptr to the context, len)并让切片超出范围
  • 写入:将取消引用的值设置为T
  • 读取:取消引用类型为 T 的指针

lib.rs

pub mod memory_operations {
    [...] Includes
    
    ///Write
    unsafe fn write_mem<T>(ptr: *mut u8, x: T) {
        let ptr = ptr as *mut T;
        unsafe {
            *ptr = x;
        }
    }
    
    /// READ
    unsafe fn read_mem<'a, T>(ptr: *mut u8) -> &'a mut T {
        let ptr = ptr as *mut T;
        unsafe {
            &mut *ptr
        }
    }

    /// Allocate
    unsafe fn allocate_memory<T>() -> MemorySpace {
        let size = std::mem::size_of::<T>();
        let mut memory = Vec::with_capacity(size);
        let ptr: *mut u8 = memory.as_mut_ptr();
        std::mem::forget(memory);
        MemorySpace::new(ptr as *mut T)
    }

    /// Deallocate without getting T back and without calling the deconstructor
    unsafe fn deallocate_memory_raw(memory: MemorySpace) {
        let buffer = unsafe {
            std::slice::from_raw_parts_mut(memory.ptr(), memory.size())
        };
        drop(buffer);
    }

    /// Deallocate a Layout in memory as a type T and get that type T back
    unsafe fn deallocate_memory_type<'a, T>(memory: MemorySpace) -> &'a mut T {
        let ptr = memory.ptr() as *mut T;
        println!("Dealloc Type");
        unsafe { &mut *ptr }
    }
    
    #[derive(Debug, Clone, Copy)]
    /// Simple Wrapper for Layout and pointer to pass it to JS
    pub struct MemorySpace {
        offset: *mut u8, 
        size: usize,
        align: usize,
    }

    impl MemorySpace {
        pub fn new<T: Sized>(offset: *mut T) -> MemorySpace {
            let layout = 
            unsafe { alloc::Layout::for_value_raw(offset) };

            let offset = offset as *mut u8;
            MemorySpace {offset, size: layout.size(), align: layout.align()}
        }

        pub fn ptr(&self) -> *mut u8 {self.offset}
        pub fn size(&self) -> usize {self.size}
        pub fn align(&self) -> usize {self.align}

        pub fn layout(&self) -> Result<Layout, LayoutError> {
            Layout::from_size_align(self.size, self.align)
        }
    }
}

当前写入时失败,似乎只是不分配或分配和释放内存。我目前只是将其作为二进制文件而不是 WASM 文件进行测试,以便我可以提前调试问题。

问题
我想知道这是否是编译为 wasm 时访问内存的正确方法(如果您认为这个内存问题很愚蠢,请告诉我)。如果是这种情况,我该如何修复此代码以作为二进制文件和 wasm 文件运行?

rust memory webassembly
1个回答
0
投票

您提到您正在使用

wasm-bindgen
wasm-pack
构建 JS/Rust 游戏。为什么不简单地使用
wasm-bindgen
来进行内存共享呢?如here所述,它允许您注释
struct
和方法,以便它们可以传递给JS并具有类似对象的接口,其中方法调用Rust方法。对象本身仍然分配在 WASM 内存中。

如果您出于某种原因想自己执行此操作,那么至少看看

wasm-bindgen
在 JS 粘合代码中生成了什么。它创建了许多由
Uint8Array
支持的类型化数组,例如
wasm.memory.buffer
。然后通过写入或读取此类型化数组来完成对 WASM 内存的原始访问。这也是粘合方法的作用。

使用你的代码,我能够创建一个

MemorySpace
,将其传递给JS函数,JS函数写入一些数据,然后Rust可以将其读回。我在
#[wasm_bindgen]
及其
MemorySpace
块上使用了
impl

cafce25指出了您的释放方法中的一个问题:删除

std::slice::from_raw_parts_mut
的结果不会执行任何操作,因为切片不拥有。您想使用
Vec
 恢复完整的 
Vec::from_raw_parts
,然后将其删除。

我还要注意,

MemorySpace
应该在其
Drop
处理程序中解除分配,而不是使用您可能忘记调用的单独的解除分配方法。同样,您应该确保保留对传递给 JS 的
MemorySpace
的引用,只要它可用即可。

© www.soinside.com 2019 - 2024. All rights reserved.