Skip to content

Commit 1446d79

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

File tree

3 files changed

+77
-78
lines changed

3 files changed

+77
-78
lines changed

examples/gcd_perf.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import wasmtime.loader
2+
import time
3+
from math import gcd as math_gcd
4+
from gcd import gcd_func as wasm_gcd
5+
from gcd_alt import gcd as wasm_gcd_alt
6+
7+
def python_gcd(x, y):
8+
while y:
9+
x, y = y, x % y
10+
return abs(x)
11+
12+
N = 1_000
13+
by_name = locals()
14+
for name in 'math_gcd', 'python_gcd', 'wasm_gcd', 'wasm_gcd_alt':
15+
gcdf = by_name[name]
16+
start_time = time.perf_counter()
17+
for _ in range(N):
18+
g = gcdf(16516842, 154654684)
19+
total_time = time.perf_counter() - start_time
20+
print(total_time, "\t\t", name)

wasmtime/_func.py

Lines changed: 4 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,19 @@
11
from contextlib import contextmanager
22
from ctypes import POINTER, byref, CFUNCTYPE, c_void_p, cast
33
import ctypes
4-
from wasmtime import Store, FuncType, Val, IntoVal, Trap, WasmtimeError, ValType
4+
from wasmtime import Store, FuncType, Val, IntoVal, Trap, WasmtimeError
55
from . import _ffi as ffi
66
from ._extern import wrap_extern
77
from typing import Callable, Optional, Generic, TypeVar, List, Union, Tuple, cast as cast_type, Sequence
88
from ._exportable import AsExtern
99
from ._store import Storelike
10-
from ._bindings import wasmtime_val_raw_t, wasm_valtype_kind, wasmtime_val_t, wasmtime_externref_t, wasmtime_func_t
11-
from ._value import _unintern
12-
from ._ffi import (
13-
WASMTIME_I32,
14-
WASMTIME_I64,
15-
WASMTIME_F32,
16-
WASMTIME_F64,
17-
WASMTIME_V128,
18-
WASMTIME_FUNCREF,
19-
WASMTIME_EXTERNREF,
20-
WASM_ANYREF,
21-
WASM_FUNCREF,
22-
wasmtime_externref_data,
23-
)
24-
10+
from ._bindings import wasmtime_val_raw_t
11+
from ._value import get_valtype_attr, val_getter, val_setter
2512

2613
T = TypeVar('T')
2714
FUNCTIONS: "Slab[Tuple]"
2815
LAST_EXCEPTION: Optional[Exception] = None
2916

30-
val_id2attr = {
31-
WASMTIME_I32.value: 'i32',
32-
WASMTIME_I64.value: 'i64',
33-
WASMTIME_F32.value: 'f32',
34-
WASMTIME_F64.value: 'f64',
35-
WASMTIME_V128.value: 'v128',
36-
WASMTIME_FUNCREF.value: 'funcref',
37-
WASMTIME_EXTERNREF.value: 'externref',
38-
WASM_FUNCREF.value: 'funcref',
39-
WASM_ANYREF.value: 'externref',
40-
}
41-
42-
def get_valtype_attr(ty: ValType):
43-
return val_id2attr[wasm_valtype_kind(ty._ptr)]
44-
45-
from struct import Struct
46-
47-
def val_getter(store_id, val_raw, attr):
48-
val = getattr(val_raw, attr)
49-
50-
if attr=='externref':
51-
ptr = ctypes.POINTER(wasmtime_externref_t)
52-
if not val: return None
53-
ffi = ptr.from_address(val)
54-
if not ffi: return
55-
extern_id = wasmtime_externref_data(ffi)
56-
ret = _unintern(extern_id)
57-
return ret
58-
elif attr=='funcref':
59-
if val==0: return None
60-
f=wasmtime_func_t()
61-
f.store_id=store_id
62-
f.index=val
63-
ret=Func._from_raw(f)
64-
return ret
65-
return val
66-
67-
def val_setter(dst, attr, val):
68-
if attr=='externref':
69-
if isinstance(val, Val) and val._raw.kind==WASMTIME_EXTERNREF.value:
70-
if val._raw.of.externref:
71-
extern_id = wasmtime_externref_data(val._raw.of.externref)
72-
casted = ctypes.addressof(val._raw.of.externref)
73-
else:
74-
v = Val.externref(val)
75-
casted = ctypes.addressof(v._raw.of.externref)
76-
elif attr=='funcref':
77-
if isinstance(val, Val) and val._raw.kind==WASMTIME_FUNCREF.value:
78-
casted = val._raw.of.funcref.index
79-
else: raise RuntimeError("foo")
80-
elif isinstance(val, Func):
81-
# TODO: handle null_funcref
82-
# TODO: validate same val._func.store_id
83-
casted = val._func.index
84-
else:
85-
if isinstance(val, Val):
86-
casted = getattr(val._raw.of, attr)
87-
else:
88-
casted = val
89-
setattr(dst, attr, casted)
9017

9118
class Func:
9219
_func: ffi.wasmtime_func_t
@@ -147,8 +74,7 @@ def _extract_return(self, vals_raw: ctypes.Array[wasmtime_val_raw_t]) -> Union[I
14774
if self._results_n==0:
14875
return None
14976
if self._results_n==1:
150-
ret = val_getter(self._func.store_id, vals_raw[0], self._results_str0)
151-
return ret
77+
return val_getter(self._func.store_id, vals_raw[0], self._results_str0)
15278
# we can use tuple construct, but I'm using list for compatability
15379
return [val_getter(self._func.store_id, val_raw, ret_str) for val_raw, ret_str in zip(vals_raw, self._results_str)]
15480

wasmtime/_value.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@
44
import ctypes
55
import typing
66

7+
val_id2attr = {
8+
WASMTIME_I32.value: 'i32',
9+
WASMTIME_I64.value: 'i64',
10+
WASMTIME_F32.value: 'f32',
11+
WASMTIME_F64.value: 'f64',
12+
WASMTIME_V128.value: 'v128',
13+
WASMTIME_FUNCREF.value: 'funcref',
14+
WASMTIME_EXTERNREF.value: 'externref',
15+
WASM_FUNCREF.value: 'funcref',
16+
WASM_ANYREF.value: 'externref',
17+
}
718

819
@ctypes.CFUNCTYPE(None, c_void_p)
920
def _externref_finalizer(extern_id: int) -> None:
@@ -24,6 +35,48 @@ def _intern(obj: typing.Any) -> c_void_p:
2435
def _unintern(val: int) -> typing.Any:
2536
return Val._id_to_extern.get(val)
2637

38+
def get_valtype_attr(ty: ValType) -> str:
39+
return val_id2attr[wasm_valtype_kind(ty._ptr)]
40+
41+
def val_getter(store_id: int, val_raw: wasmtime_val_raw_t, attr: str) -> typing.Union[int, float, "wasmtime.Func", typing.Any]:
42+
val = getattr(val_raw, attr)
43+
44+
if attr=='externref':
45+
ptr = ctypes.POINTER(wasmtime_externref_t)
46+
if not val: return None
47+
ffi = ptr.from_address(val)
48+
if not ffi: return None
49+
extern_id = wasmtime_externref_data(ffi)
50+
return _unintern(extern_id)
51+
elif attr=='funcref':
52+
if val==0: return None
53+
f = wasmtime_func_t()
54+
f.store_id = store_id
55+
f.index = val
56+
return wasmtime.Func._from_raw(f)
57+
return val
58+
59+
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:
62+
casted = ctypes.addressof(val._raw.of.externref)
63+
else:
64+
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:
67+
casted = val._raw.of.funcref.index
68+
elif isinstance(val, wasmtime.Func):
69+
# TODO: validate same val._func.store_id
70+
casted = val._func.index
71+
else:
72+
raise RuntimeError("foo")
73+
else:
74+
if isinstance(val, Val):
75+
casted = getattr(val._raw.of, attr)
76+
else:
77+
casted = val
78+
setattr(dst, attr, casted)
79+
2780

2881
class Val:
2982
# We can't let the extern values we wrap `externref`s around be GC'd, so we

0 commit comments

Comments
 (0)