问题
To pass typed array from emscripten'ed C++ to javascript I came up with this code
#include <emscripten/bind.h>
#include <emscripten/val.h>
auto test(const emscripten::val &input) {
const auto data = emscripten::convertJSArrayToNumberVector<float>(input); // copies data
// generate output in some form
std::vector<float> output = { 1, 2, 3 };
// make a typed array view of the output
emscripten::val view{ emscripten::typed_memory_view(output.size(), output.data()) };
// create new typed array to return
auto result = emscripten::val::global("Float32Array").new_(output.size());
// copy data from generated output to return object
result.call<void>("set", view);
return result;
}
EMSCRIPTEN_BINDINGS(KissFft) {
emscripten::function("test", &test);
}
(build with em++ test.cpp -o test.html --bind
)
In this case there are two extra copies:
- the copy from input array to wasm memory, as far as I understand it's unavoidable;
const auto data = emscripten::convertJSArrayToNumberVector<float>(input);
- the copy from wasm memory to javascript object:
emscripten::val view{ emscripten::typed_memory_view(output.size(), output.data()) }; auto result = emscripten::val::global("Float32Array").new_(output.size()); result.call<void>("set", view); return result;
Is there a way to avoid extra copy from generated output to javascript object in the second case?
I'm aware of the possibility to return memory view like this:
std::vector<float> output;
auto test(const emscripten::val &input) {
const auto data = emscripten::convertJSArrayToNumberVector<float>(input);
//generate output
return emscripten::val{ emscripten::typed_memory_view(output.size(), output.data()) };
}
EMSCRIPTEN_BINDINGS(KissFft) {
emscripten::function("test", &test);
}
But in this case the returned object refers to the underlying memory owned by output
static object with all the consequences, like modifying the memory on the C++ side, or even deallocating it.
回答1:
I was thinking to go lower a bit. Putting here (instead of comment) to paste snippets (taken from embind examples from emscripten, here in C but you may do the same with C++).
// quick_example.cpp
#include <emscripten/bind.h>
#include <stdio.h>
using namespace emscripten;
struct buffer {
unsigned int pointer;
unsigned int size;
};
buffer lerp() {
buffer myBuffer;
unsigned int size = 10;
float * myTab = (float*)malloc(size * sizeof(float));
for (int i = 0; i < size; i++) {
myTab[i] = 2.5 * i;
printf(" Native side index: %d value: %f address: %p\n", i, myTab[i], &myTab[i]);
}
myBuffer.pointer = (unsigned int) myTab;
myBuffer.size = size;
printf(" Native side pointer: %p size: %d\n", myTab, size);
return myBuffer;
}
EMSCRIPTEN_BINDINGS(my_module) {
value_array<buffer>("buffer")
.element(&buffer::pointer)
.element(&buffer::size)
;
function("lerp", &lerp);
}
index.html - here in js you may copy, make a view and finally free the memory what (as I understood was one of the problems from above?)
<!doctype html>
<html>
<script>
var Module = {
onRuntimeInitialized: function() {
var result = Module.lerp();
console.log(" JS side pointer: " + result[0] + " size: " + result[1]);
for (var i = 0; i < result[1]; i++) {
console.log("index: " + i + " value: " + Module.HEAPF32[(result[0] + i * 4) / 4] + " pointerInc: " + (result[0] + i * 4));
}
Module._free(result[0]);
}
};
</script>
<script src="lerp.js"></script>
</html>
build command
emcc --bind -o lerp.js lerp.cpp
来源:https://stackoverflow.com/questions/65566923/is-there-a-more-efficient-way-to-return-arrays-from-c-to-javascript