From 12011f453773db4c670983e65e5eaffe73c0d94f Mon Sep 17 00:00:00 2001 From: Ingvar Stepanyan Date: Mon, 25 Mar 2019 17:42:10 +0000 Subject: [PATCH] Add specialised passStringToWasm for Node.js Node.js doesn't currently implement `TextEncoder::encodeInto`. I've raised an upstream issue to add it - https://github.com/nodejs/node/issues/26904 - but it's likely to take some time and will be available only in new releases. In the meanwhile, it's worth noting that Node.js already has `Buffer::write` which has pretty similar semantics, but doesn't require creating an intermediate view using `.subarray` and instead accepts pointer and length directly. Also, Node.js has `Buffer::byteLength` helper which allows to efficiently retrieve an encoded byte length of a string upfront, and so allows us to avoid a loop with reallocations. This change takes leverage of these methods by generating an additional Buffer-based view into the WASM memory and using it for string operations. I'm seeing up to 35% increase in performance in string-heavy library benchmarks. --- crates/cli-support/src/js/mod.rs | 57 ++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 80a96bb304d..5a20fd6e543 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1236,8 +1236,6 @@ impl<'a> Context<'a> { return Ok(()); } self.require_internal_export("__wbindgen_malloc")?; - self.expose_text_encoder(); - self.expose_uint8_memory(); self.expose_wasm_vector_len(); let debug = if self.config.debug { " @@ -1247,6 +1245,33 @@ impl<'a> Context<'a> { "" }; + // If we are targeting Node.js, it doesn't have `encodeInto` yet + // but it does have `Buffer::write` which has similar semantics but + // doesn't require creating intermediate view using `subarray` + // and also has `Buffer::byteLength` to calculate size upfront. + if self.config.mode.nodejs() { + self.expose_node_buffer_memory(); + + self.global(&format!( + " + function passStringToWasm(arg) {{ + {} + const size = Buffer.byteLength(arg); + const ptr = wasm.__wbindgen_malloc(size); + getNodeBufferMemory().write(arg, ptr, size); + WASM_VECTOR_LEN = size; + return ptr; + }} + ", + debug, + )); + + return Ok(()); + } + + self.expose_text_encoder(); + self.expose_uint8_memory(); + // The first implementation we have for this is to use // `TextEncoder#encode` which has been around for quite some time. let use_encode = format!( @@ -1600,48 +1625,52 @@ impl<'a> Context<'a> { )); } + fn expose_node_buffer_memory(&mut self) { + self.memview("getNodeBufferMemory", "Buffer.from"); + } + fn expose_int8_memory(&mut self) { - self.memview("getInt8Memory", "Int8Array"); + self.memview("getInt8Memory", "new Int8Array"); } fn expose_uint8_memory(&mut self) { - self.memview("getUint8Memory", "Uint8Array"); + self.memview("getUint8Memory", "new Uint8Array"); } fn expose_clamped_uint8_memory(&mut self) { - self.memview("getUint8ClampedMemory", "Uint8ClampedArray"); + self.memview("getUint8ClampedMemory", "new Uint8ClampedArray"); } fn expose_int16_memory(&mut self) { - self.memview("getInt16Memory", "Int16Array"); + self.memview("getInt16Memory", "new Int16Array"); } fn expose_uint16_memory(&mut self) { - self.memview("getUint16Memory", "Uint16Array"); + self.memview("getUint16Memory", "new Uint16Array"); } fn expose_int32_memory(&mut self) { - self.memview("getInt32Memory", "Int32Array"); + self.memview("getInt32Memory", "new Int32Array"); } fn expose_uint32_memory(&mut self) { - self.memview("getUint32Memory", "Uint32Array"); + self.memview("getUint32Memory", "new Uint32Array"); } fn expose_int64_memory(&mut self) { - self.memview("getInt64Memory", "BigInt64Array"); + self.memview("getInt64Memory", "new BigInt64Array"); } fn expose_uint64_memory(&mut self) { - self.memview("getUint64Memory", "BigUint64Array"); + self.memview("getUint64Memory", "new BigUint64Array"); } fn expose_f32_memory(&mut self) { - self.memview("getFloat32Memory", "Float32Array"); + self.memview("getFloat32Memory", "new Float32Array"); } fn expose_f64_memory(&mut self) { - self.memview("getFloat64Memory", "Float64Array"); + self.memview("getFloat64Memory", "new Float64Array"); } fn memview_function(&mut self, t: VectorKind) -> &'static str { @@ -1711,7 +1740,7 @@ impl<'a> Context<'a> { let cache{name} = null; function {name}() {{ if (cache{name} === null || cache{name}.buffer !== {mem}.buffer) {{ - cache{name} = new {js}({mem}.buffer); + cache{name} = {js}({mem}.buffer); }} return cache{name}; }}