Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

need example of sharing memory between JS and Rust #2456

Closed
mvolkmann opened this issue Feb 17, 2021 · 12 comments
Closed

need example of sharing memory between JS and Rust #2456

mvolkmann opened this issue Feb 17, 2021 · 12 comments
Labels

Comments

@mvolkmann
Copy link

I want to create a Float64Array in JS code and make it available to a Rust function through shared memory without passing/copying it. I've spent quite a bit of time looking for an example of doing this and haven't found one. Does wasm-bindgen support this? If so, can you point me to an example?

@chinedufn
Copy link
Contributor

Hey there.

I think that this should do the trick -> https://rustwasm.github.io/wasm-bindgen/reference/types/pointers.html

@chinedufn
Copy link
Contributor

chinedufn commented Feb 17, 2021

Sorry let me clarify.

One approach off the top of my head would be to:

  • Allocate a Vec<f32> on the Rust side with whatever capacity that you need and return a pointer to it *mut f64.

  • Create a Float64Array on the JS side using the wasm memory buffer (see the example linked above). The second and third argument for a new typed array allow you to specify the start and length within the underlying buffer. So use the pointer as the start and the length as whatever capacity you allocated in the bullet point above.

Let me know if you have any questions. Best of luck.

@mvolkmann
Copy link
Author

@chinedufn Thanks so much! I think I'm close to having this working. After populating the shared memory on the JS side I pass the pointer and the number of elements to the Rust side. In the Rust code I can iterate over the numbers, but I don't know how to get them. I assume I have to do something like this with an unsafe block.

#[wasm_bindgen]
pub fn sum(ptr: *mut f64, count: usize) -> f64 {
    let mut sum = 0.0;
    unsafe {
        for i in 0..count {
            let number = ptr[i * 8] as f64; // THIS LINE IS WRONG!
            sum += number; 
        }
    }
    sum
}

@mvolkmann
Copy link
Author

@chinedufn
Copy link
Contributor

chinedufn commented Feb 17, 2021

Potential reasons:

  • You're allocating memory in your Rust microbenchmark, but not in JS

    /// Efficiently copies the contents of this JS typed array into a new Vec.
    pub fn to_vec(&self) -> Vec<$ty> {
    let mut output = vec![$ty::default(); self.length() as usize];
    self.raw_copy_to(&mut output);
    output
    }

  • There is overhead with going from JS -> Wasm -> JS

  • Your JS engine might be doing a good job of optimizing your sum function

  • You are not compiling your Rust code in release mode

You might be running into any number of those.

Glad it all works. If you're in good shape feel free to close this issue.

Good luck.

@chinedufn
Copy link
Contributor

chinedufn commented Feb 17, 2021

Also you'll want to store that Vec<f64> somewhere. You're dropping it and thus freeing the memory that backs it.

Something like:

#[wasm_bindgen]
struct MyApp {
    my_data: Vec<f32>
}

#[wasm_bindgen]
impl MyApp {
    #[wasm_bindgen(constructor)]
    fn new (capacity: usize) -> Self {
        MyApp { my_data: Vec::with_capacity(capacity) }
    }
}

Then when you are summing you don't need to allocate a new vector.

Although, as alluded to above, summing might not be the most informative/realistic/useful benchmark. I'm just sharing some tips under the assumption that you're mainly just experimenting.

@mvolkmann
Copy link
Author

Interesting. If I use this approach of preallocating the vector by creating an instance of MyApp where does that instance get created ... that is where should I place the call to MyApp::new()?

@chinedufn
Copy link
Contributor

@chinedufn
Copy link
Contributor

If you haven't already, have a look at the game of life tutorial https://rustwasm.github.io/book/introduction.html .

@domoritz
Copy link
Contributor

Also see #1643 and #1993 (comment).

@domoritz
Copy link
Contributor

Here is what I did. I allocate a vector is WASM and then pass it as a view (via view_mut_raw) to JS. I can then write a function that expects an WasmUint8Array and reads its buffer (e.g. data.0).

#[wasm_bindgen]
pub struct WasmUint8Array(Vec<u8>);

#[wasm_bindgen]
impl WasmUint8Array {
    #[wasm_bindgen(constructor)]
    pub fn new(size: usize) -> Self {
        let buffer = vec![0; size];
        Self { 0: buffer }
    }

    #[wasm_bindgen(getter, js_name = buffer)]
    pub fn buffer(&mut self) -> js_sys::Uint8Array {
        unsafe { js_sys::Uint8Array::view_mut_raw(self.0.as_mut_ptr(), self.0.len()) }
    }
}

@alexcrichton
Copy link
Contributor

I think everything here looks like it's been answered and handled (thanks @chinedufn!), so I'm going to close.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants