arangodb / arangodb

🥑 ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions.
https://www.arangodb.com
Other
13.54k stars 839 forks source link

Does foxx support wasm modules? #7334

Open TZubiri opened 5 years ago

TZubiri commented 5 years ago

If so must they be compiled to javascript first? Or can wasm be compiled to bytecode? Are there any examples?

Thank you!

Simran-B commented 5 years ago

Going from WebAssembly to something else like JavaScript is not a common use case and not what WebAssembly is about. It "is a binary instruction format [...]. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust" (source).

So you write code in e.g. Rust, compile it to .wasm (bytecode) and can use it in browsers or Node.js. There is no parsing like with text-based JavaScript code (which is kinda slow), but there is still some decoding to do to make it run on your machine (pretty fast). This is intended: WebAssembly is supposed to be portable, which means you can't compile it to machine code to run directly, because then it would be limited to a particular computer architecture it can run on.

There is actually a tool called wasm2c which can transpile .wasm to C code, which you could then compile natively. But then you're outside the web platform. And if your code was written in e.g. Rust, then it would be by far easier to compile from Rust to native code directly.

To the actual question: ArangoDB 3.2-3.4 embed V8 in version 5.7.492.77. Based on a post on StackOverflow, this is apparently one of the first versions that supports WebAssembly. I conducted a little test and got something working. So generally speaking, it is possible. However, you should be careful as this feature isn't tested so far, expect there to be a cost for calls and data transfer between JS <-> WASM and keep in mind that V8 features for WebAssembly like streamed compilation and recent optimizations (e.g. Liftoff) are not in ArangoDB at this point in time. A future version may come with a newer V8 version though.

If you look for a way to do heavy computation in ArangoDB with as little overhead as possible, then you should probably implement the required code in C++ and compile everything from source. There you have ways to run computations in multiple threads etc., whereas Foxx doesn't support the child_process module, WebAssembly Threads v1 has no native threads in the specs and a preview is only available in V8 7.0 anyway.


I loosely followed a Rust and WebAssembly tutorial to set up the necessary tools to create a wasm module. It exports a single function which takes two integers, adds them up and returns the result as integer:

src/lib.rs

// ...
#[wasm_bindgen]
pub fn calc(a: i32, b: i32) -> i32 {
    return a + b;
}

I used wasm-pack build --target nodejs to build the module. It resulted in a 601 bytes .wasm file. There will be a *_bg.js file which contains the glue code to load and instantiate the module, but I had to change it a little to make it work:

_pkg/wasm_foxx_testbg.js

const path = require('path').join(__dirname, 'wasm_foxx_test_bg.wasm');
const bytes = require('fs').readFileSync(path);
let imports = {};

const wasmModule = new WebAssembly.Module(new Uint8Array(bytes)); // edited
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
module.exports = wasmInstance.exports;

Without new Uint8Array(...) I got the following error, even though bytes is a Buffer:

TypeError: WebAssembly.Module(): Argument 0 must be a buffer source

index.js (_pkg/wasm_foxx_testbg.js copied as index.js into new folder _/wasm_foxxtest in Foxx source root, together with the file _/pkg/wasm_foxx_testbg.wasm)

'use strict';

const createRouter = require("@arangodb/foxx/router");
const joi = require('joi');
const wasm = require("./wasm_foxx_test");

const router = createRouter();

router.get('/:x/:y', function (req, res) {
  res.send(wasm.calc(req.pathParams.x, req.pathParams.y));
})
.pathParam('x', joi.number())
.pathParam('y', joi.number())
.response(joi.number().integer(), 'An integer')
.summary('Return externally calculated integer')
.description('Calls WebAssembly function to add up two integers');

module.context.use(router);

foxx-calc-service.zip

I used foxx install /calc-service (Foxx-CLI) to deploy the service.

URL to test the service: http://localhost:8529/_db/_system/calc-service/4/7.8

It displays 11. Note how 7.8 is truncated to 7 because calc() is defined for integers in the Rust code.

mpoeter commented 5 years ago

@Simran-B interesting results! I was wondering whether ArangoDB supports wasm for some time as well, but for a different reason.

I know that you have plans to add support for "custom analyzers" for ArangoSearch, but AFAIK it is not yet clear how these should be implemented and executed. I was thinking that maybe wasm could be the perfect solution:

And with the V8 engine you already have the required runtime environment. Just a tought... PS: sorry for highjacking this issue!

Simran-B commented 5 years ago

@mpoeter True, wasm is a pretty interesting candidate for user plugins. I guess it comes down to whether you need things like multiple threads and how much overhead there is if you end up with something like C++ <-> V8 <-> wasm linear memory. I'm no V8 expert, so don't know if it's feasible and how the performance is, but seems worth to investigate.

Some use cases for plugins that come to mind:

Simran-B commented 5 years ago

Once https://github.com/arangodb/arangodb/pull/9072 is merged, we should retry without the workaround new Uint8Array(...).

TZubiri commented 5 years ago

Hey Simran, I'm glad Wasm is being adopted. However I can't offer to test this as I don't remember what I needed it for.

Hopefully someone else might offer to be a beta tester of wasm modules.

Simran-B commented 5 years ago

It still works as before in my test using an up-to-date devel build (623048a). new Uint8Array() and the usage of WebAssembly.Module() is required.