Skip to content

Commit 1ca73e9

Browse files
FIXES #137: make calling wasm from python 7x faster
1 parent 1446d79 commit 1ca73e9

File tree

6 files changed

+65
-26
lines changed

6 files changed

+65
-26
lines changed

examples/gcd.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# Example of instantiating a wasm module and calling an export on it
22

33
from wasmtime import Store, Module, Instance
4-
4+
from functools import partial
55
store = Store()
66
module = Module.from_file(store.engine, './examples/gcd.wat')
77
instance = Instance(store, module, [])
88
gcd = instance.exports(store)["gcd"]
9-
9+
gcd_func = partial(gcd, store)
1010
print("gcd(6, 27) = %d" % gcd(store, 6, 27))
11+
print("gcd(6, 27) = %d" % gcd_func(6, 27))

examples/gcd_perf.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@
44
from gcd import gcd_func as wasm_gcd
55
from gcd_alt import gcd as wasm_gcd_alt
66

7+
78
def python_gcd(x, y):
89
while y:
910
x, y = y, x % y
1011
return abs(x)
1112

13+
1214
N = 1_000
1315
by_name = locals()
14-
for name in 'math_gcd', 'python_gcd', 'wasm_gcd', 'wasm_gcd_alt':
16+
for name in "math_gcd", "python_gcd", "wasm_gcd", "wasm_gcd_alt":
1517
gcdf = by_name[name]
1618
start_time = time.perf_counter()
1719
for _ in range(N):

examples/simd_i8x16.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
how to call v128 SMID operations
2+
how to call v128 SIMD operations
33
for more details see https://github.com/WebAssembly/simd/blob/main/proposals/simd/SIMD.md#integer-addition
44
"""
55
import ctypes
@@ -8,9 +8,10 @@
88
from wasmtime import Store, Module, Instance
99

1010

11-
1211
store = Store()
13-
module = Module(store.engine, """
12+
module = Module(
13+
store.engine,
14+
"""
1415
(module
1516
(func $add_v128 (param $a v128) (param $b v128) (result v128)
1617
local.get $a
@@ -19,12 +20,14 @@
1920
)
2021
(export "add_v128" (func $add_v128))
2122
)
22-
""")
23+
""",
24+
)
2325

2426
instance = Instance(store, module, [])
25-
vector_type = ctypes.c_uint8*16
27+
vector_type = ctypes.c_uint8 * 16
2628
add_v128 = partial(instance.exports(store)["add_v128"], store)
27-
a=vector_type(*(i for i in range(16)))
28-
b=vector_type(*(40+i for i in range(16)))
29-
c=add_v128(a, b)
29+
a = vector_type(*(i for i in range(16)))
30+
b = vector_type(*(40 + i for i in range(16)))
31+
c = add_v128(a, b)
3032
print([v for v in c])
33+
print([v for v in c] == [i + j for i, j in zip(a, b)])

tests/test_func.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import unittest
2+
import ctypes
23

4+
from functools import partial
35
from wasmtime import *
46

57

@@ -17,6 +19,31 @@ def test_add(self):
1719
func = Func(store, ty, lambda a, b: a + b)
1820
self.assertEqual(func(store, 1, 2), 3)
1921

22+
def test_simd_i8x16_add(self):
23+
# i8x16.add is SIMD 128-bit vector of i8 items of size 16
24+
store = Store()
25+
module = Module(
26+
store.engine,
27+
"""
28+
(module
29+
(func $add_v128 (param $a v128) (param $b v128) (result v128)
30+
local.get $a
31+
local.get $b
32+
i8x16.add
33+
)
34+
(export "add_v128" (func $add_v128))
35+
)
36+
""",
37+
)
38+
39+
instance = Instance(store, module, [])
40+
vector_type = ctypes.c_uint8 * 16
41+
add_v128 = partial(instance.exports(store)["add_v128"], store)
42+
a = vector_type(*(i for i in range(16)))
43+
b = vector_type(*(40 + i for i in range(16)))
44+
c = add_v128(a, b)
45+
self.assertEqual([v for v in c], [i + j for i, j in zip(a, b)])
46+
2047
def test_calls(self):
2148
store = Store()
2249
ty = FuncType([ValType.i32()], [])

wasmtime/_func.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ def _create_raw_vals(self, *params: IntoVal) -> ctypes.Array[wasmtime_val_raw_t]
6969
for i, param_str in enumerate(self._params_str):
7070
val_setter(raw[i], param_str, params[i])
7171
return raw
72-
72+
7373
def _extract_return(self, vals_raw: ctypes.Array[wasmtime_val_raw_t]) -> Union[IntoVal, Sequence[IntoVal], None]:
74-
if self._results_n==0:
74+
if self._results_n == 0:
7575
return None
76-
if self._results_n==1:
76+
if self._results_n == 1:
7777
return val_getter(self._func.store_id, vals_raw[0], self._results_str0)
7878
# we can use tuple construct, but I'm using list for compatability
7979
return [val_getter(self._func.store_id, val_raw, ret_str) for val_raw, ret_str in zip(vals_raw, self._results_str)]
@@ -91,8 +91,7 @@ def _init_call(self, ty: FuncType):
9191
self._params_n = params_n
9292
self._results_n = results_n
9393
n = max(params_n, results_n)
94-
self._vals_raw_type = wasmtime_val_raw_t*n
95-
94+
self._vals_raw_type = wasmtime_val_raw_t * n
9695

9796
def __call__(self, store: Storelike, *params: IntoVal) -> Union[IntoVal, Sequence[IntoVal], None]:
9897
"""

wasmtime/_value.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
WASM_ANYREF.value: 'externref',
1717
}
1818

19+
1920
@ctypes.CFUNCTYPE(None, c_void_p)
2021
def _externref_finalizer(extern_id: int) -> None:
2122
Val._id_to_ref_count[extern_id] -= 1
@@ -35,35 +36,41 @@ def _intern(obj: typing.Any) -> c_void_p:
3536
def _unintern(val: int) -> typing.Any:
3637
return Val._id_to_extern.get(val)
3738

39+
3840
def get_valtype_attr(ty: ValType) -> str:
3941
return val_id2attr[wasm_valtype_kind(ty._ptr)]
4042

43+
4144
def val_getter(store_id: int, val_raw: wasmtime_val_raw_t, attr: str) -> typing.Union[int, float, "wasmtime.Func", typing.Any]:
4245
val = getattr(val_raw, attr)
43-
44-
if attr=='externref':
46+
47+
if attr == 'externref':
4548
ptr = ctypes.POINTER(wasmtime_externref_t)
46-
if not val: return None
49+
if not val:
50+
return None
4751
ffi = ptr.from_address(val)
48-
if not ffi: return None
52+
if not ffi:
53+
return None
4954
extern_id = wasmtime_externref_data(ffi)
5055
return _unintern(extern_id)
51-
elif attr=='funcref':
52-
if val==0: return None
56+
elif attr == 'funcref':
57+
if val == 0:
58+
return None
5359
f = wasmtime_func_t()
5460
f.store_id = store_id
5561
f.index = val
5662
return wasmtime.Func._from_raw(f)
5763
return val
5864

65+
5966
def val_setter(dst: wasmtime_val_raw_t, attr: str, val: "IntoVal"):
60-
if attr=='externref':
61-
if isinstance(val, Val) and val._raw.kind==WASMTIME_EXTERNREF.value:
67+
if attr == 'externref':
68+
if isinstance(val, Val) and val._raw.kind == WASMTIME_EXTERNREF.value:
6269
casted = ctypes.addressof(val._raw.of.externref)
6370
else:
6471
casted = ctypes.addressof(Val.externref(val)._raw.of.externref)
65-
elif attr=='funcref':
66-
if isinstance(val, Val) and val._raw.kind==WASMTIME_FUNCREF.value:
72+
elif attr == 'funcref':
73+
if isinstance(val, Val) and val._raw.kind == WASMTIME_FUNCREF.value:
6774
casted = val._raw.of.funcref.index
6875
elif isinstance(val, wasmtime.Func):
6976
# TODO: validate same val._func.store_id

0 commit comments

Comments
 (0)