Skip to content

Commit

Permalink
Basic test benchmark for WebAssembly integration. (#216)
Browse files Browse the repository at this point in the history
This is not nearly as efficient as a native build of the transform
binary, due to bugs such as http://crbug.com/853685 and
golang/go#27462, but serves as a starting
point for further investigation.

There have been a few optimizations already:
 - Keeping one long-lived process open to amortize the bootstrapping
   cost across requests.
 - Batching the "requests" to the wasm code via Promise.all. (This can
   be seen by adding a log statement just before the call to exports.run
   in wasm_exec.js.)

Another potential problem to investigate the cost of is the lack of
cross-heap GC.
  • Loading branch information
twifkak authored Dec 15, 2018
1 parent 2ea2f2d commit 3102960
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 0 deletions.
45 changes: 45 additions & 0 deletions cmd/transform_wasm/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build go1.11

/*
Binary wrapper for the transformer lib, to be run via main.js.
*/
package main

import (
"syscall/js"

rpb "github.com/ampproject/amppackager/transformer/request"
t "github.com/ampproject/amppackager/transformer"
)

func transform(args []js.Value /* url, html, cb(htmlout) */) {
r := &rpb.Request{Html: args[1].String(), DocumentUrl: args[0].String(), Config: rpb.Request_DEFAULT}
o, _, err := t.Process(r)
if err != nil {
panic(err)
}
args[2].Invoke(o + "\n")
}

func main() {
cb := js.NewCallback(transform)
defer cb.Release()
done := make(chan struct{})
donecb := js.NewCallback(func(args []js.Value) { done <- struct{}{} })
js.Global().Get("begin").Invoke(cb, donecb)
<-done
}
56 changes: 56 additions & 0 deletions cmd/transform_wasm/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Bootstrapper for transform_wasm. Transforms all html files in
// ${TESTDIR:-/tmp/amps}.
//
// To use:
// GOOS=js GOARCH=wasm go build -o transform.wasm ./cmd/transform_wasm/ &&
// node --max-old-space-size=4096 cmd/transform_wasm/main.js transform.wasm

// TODO(twifkak): Investigate slowdown over time.
// TODO(twifkak): Investigate memory usage growth.

const assert = require('assert');
const { join } = require('path');
const { spawnSync } = require('child_process');

function listRecursive(dir) {
return [].concat(...fs.readdirSync(dir, {withFileTypes: true}).map((dirent) =>
dirent.isDirectory() ? listRecursive(join(dir, dirent.name)) : join(dir, dirent.name)));
}

global.begin = async function(transform, done) {
const baseDir = process.env.TESTDIR || '/tmp/amps';
const htmlPaths = listRecursive(baseDir).filter((file) => file.endsWith('.html'));
let num = 0;
let outs = htmlPaths.map((path) =>
new Promise((resolve) => {
const html = fs.readFileSync(path);
transform('https://example.com/', html, (amphtml) => {
if (++num % 100 == 0) console.log('num = ', num);
assert(amphtml.length > 1000); // "Minimum valid AMP" is larger than 1K.
resolve(amphtml);
});
}));
console.log('Pushed all %d thunks.', htmlPaths.length);
const start = process.hrtime.bigint();
await Promise.all(outs);
const total = process.hrtime.bigint() - start;
console.log(`Took ${total} nanoseconds, or ${Number(total) / htmlPaths.length / 1000000} millis per doc.`);
done();
}

const goroot = process.env.GOROOT || spawnSync('go', ['env', 'GOROOT']).stdout.toString().trim();
require(join(goroot, 'misc/wasm/wasm_exec.js'));

0 comments on commit 3102960

Please sign in to comment.