Skip to content
This repository was archived by the owner on Jun 10, 2025. It is now read-only.

Commit 2bd66b9

Browse files
authored
Add basic benchmarking suite (#106)
1 parent 60710de commit 2bd66b9

6 files changed

Lines changed: 139 additions & 5 deletions

File tree

.circleci/config.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ jobs:
7272
- image: circleci/python:3.7
7373
environment:
7474
TOXENV: py37-spec
75+
py36-benchmark:
76+
<<: *common
77+
docker:
78+
- image: circleci/python:3.6
79+
environment:
80+
TOXENV: py36-benchmark
81+
py37-benchmark:
82+
<<: *common
83+
docker:
84+
- image: circleci/python:3.7
85+
environment:
86+
TOXENV: py37-benchmark
7587
workflows:
7688
version: 2
7789
test:
@@ -82,3 +94,5 @@ workflows:
8294
- py37-core
8395
- py36-spec
8496
- py37-spec
97+
- py36-benchmark
98+
- py37-benchmark

scripts/benchmark/bench.wasm

147 Bytes
Binary file not shown.

scripts/benchmark/bench.wast

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
(module
2+
(export "thunk" (func $thunk))
3+
(export "call_thunk" (func $call_thunk))
4+
(export "add" (func $add))
5+
(export "call_add" (func $call_add))
6+
7+
(func $call_thunk (param i32)
8+
block
9+
get_local 0
10+
i32.eqz
11+
br_if 0
12+
loop
13+
call $thunk
14+
get_local 0
15+
i32.const 1
16+
i32.sub
17+
tee_local 0
18+
br_if 0
19+
end
20+
end
21+
)
22+
23+
(func $call_add (param i32) (param i32) (param i32)
24+
block
25+
get_local 0
26+
i32.eqz
27+
br_if 0
28+
loop
29+
get_local 2
30+
get_local 1
31+
call $add
32+
drop
33+
get_local 0
34+
i32.const 1
35+
i32.sub
36+
tee_local 0
37+
br_if 0
38+
end
39+
end
40+
)
41+
42+
(func $thunk)
43+
44+
(func $add (param i32) (param i32) (result i32)
45+
get_local 0
46+
get_local 1
47+
i32.add
48+
)
49+
)

scripts/benchmark/run.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from pathlib import Path
2+
import time
3+
4+
import numpy
5+
6+
from wasm import (
7+
Runtime,
8+
)
9+
10+
11+
# From https://github.com/alexcrichton/rust-wasm-benchmark/blob/master/raw-wasm/raw.wast
12+
BASE_DIR = Path(__file__).parent
13+
WASM_BENCH = BASE_DIR / 'bench.wasm'
14+
15+
16+
def _get_fn_address(runtime, module, function_name):
17+
for export in module.exports:
18+
if export.is_function and export.name == function_name:
19+
return export.function_address
20+
else:
21+
raise Exception(f"No function found by name: {function_name}")
22+
23+
24+
def bench():
25+
runtime = Runtime()
26+
module, _ = runtime.instantiate_module(runtime.load_module(WASM_BENCH))
27+
runtime.register_module('bench', module)
28+
29+
thunk_delta = bench_fn(runtime, module, 'thunk', (), 10000)
30+
add_delta = bench_fn(runtime, module, 'add', (numpy.uint32(1), numpy.uint32(2)), 10000)
31+
call_thunk_delta = bench_fn(runtime, module, 'call_thunk', (numpy.uint32(10000),), 1)
32+
call_add_delta = bench_fn(
33+
runtime,
34+
module,
35+
'call_add',
36+
(numpy.uint32(10000), numpy.uint32(1), numpy.uint32(2)),
37+
1,
38+
)
39+
40+
print(f"wrapped thunk: {call_thunk_delta / 10000:.8f} per call")
41+
print(f"wrapped add : {call_add_delta / 10000:.8f} per call")
42+
43+
thunk_overhead = (call_thunk_delta - thunk_delta)
44+
add_overhead = call_add_delta - add_delta
45+
print(f"call overhead for thunk: {thunk_overhead:.4f} ({100 * thunk_overhead / thunk_delta:.2f}%) | {thunk_overhead / 10000:.8f} per call") # noqa: E501
46+
print(f"call overhead for add : {add_overhead:.4f} ({100 * add_overhead / add_delta:.2f}%) | {add_overhead / 10000:.8f} per call") # noqa: E501
47+
48+
49+
def bench_fn(runtime, module, fn_name, fn_args, times):
50+
func_addr = _get_fn_address(runtime, module, fn_name)
51+
start_at = time.perf_counter()
52+
for _ in range(times):
53+
runtime.invoke_function(func_addr, fn_args)
54+
end_at = time.perf_counter()
55+
delta = end_at - start_at
56+
signature = f"{fn_name}{fn_args}"
57+
print(f"{signature.ljust(22)} x {str(times).rjust(10)}: {delta:.4f} | {delta / times:.4f} per-call") # noqa: E501
58+
return delta
59+
60+
61+
if __name__ == '__main__':
62+
bench()

tox.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tox]
22
envlist=
3-
py{36,37,py3}-{core,spec}
3+
py{36,37,py3}-{core,spec,benchmark}
44
lint
55
doctest
66

@@ -24,6 +24,7 @@ usedevelop=True
2424
commands=
2525
core: pytest {posargs:tests/core}
2626
spec: pytest {posargs:tests/spec}
27+
benchmark: python scripts/benchmark/run.py
2728
doctest: make -C {toxinidir}/docs doctest
2829
basepython =
2930
doctest: python

wasm/tools/fixtures/runner.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pathlib import (
55
Path,
66
)
7+
import time
78
from typing import (
89
Any,
910
Callable,
@@ -386,22 +387,29 @@ def run_fixture_test(fixture_path: Path,
386387

387388
module = None
388389

389-
logger.info("Finished test fixture: %s", fixture.file_path.name)
390+
logger.info("Test fixture: %s", fixture.file_path.name)
390391

391392
all_modules = copy.copy(runtime.modules)
392393

393394
for idx, command in enumerate(fixture.commands):
394-
logger.debug("running command: line=%d type=%s", command.line, str(type(command)))
395+
logger.info("command: line=%d type=%s", command.line, str(type(command)))
395396

396397
command_fn = get_command_fn(command)
397398

399+
start_at = time.perf_counter()
398400
result = command_fn(command, module, all_modules, runtime)
401+
end_at = time.perf_counter()
399402

400403
if result:
401404
module = result
402-
logger.debug("Finished command: line=%d type=%s", command.line, str(type(command)))
405+
logger.info(
406+
"finished command: line=%d type=%s took=%f",
407+
command.line,
408+
str(type(command)),
409+
end_at - start_at,
410+
)
403411

404412
if stop_after is not None and command.line >= stop_after:
405413
break
406414

407-
logger.debug("Finished test fixture: %s", fixture.file_path.name)
415+
logger.info("Finished test fixture: %s", fixture.file_path.name)

0 commit comments

Comments
 (0)