Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/wapython
Path: blob/main/core/wasm-opt/src/cowasm_opt.py
1067 views
1
#!/usr/bin/env python3
2
"""
3
Use this script to run wasm-opt on all the wasm binaries in a directory tree.
4
This may take a while, but is useful to do before publication, in order to
5
reduce the size of wasm files and possibly make them faster.
6
7
USAGE:
8
9
cowasm-opt [-options to wasm-opt, e.g., --asyncify] [path] ...
10
11
It will walk the path's and identify all the wasm binaries, where a file is
12
considered a wasm executable if:
13
14
- it is executable or ends with .so (python extension module), and
15
- the file contents start with b'\x00asm'
16
17
In particular, we do not consider the filename extension.
18
19
OBSERVATIONS:
20
21
- For our static kernel, wasm-opt decreases the size by 11%.
22
- For most of our dynamic libraries (e.g., python extension modules),
23
wasm-opt decreases the size by less than 3%.
24
For numpy it actually makes it larger (?).
25
- For python.wasm the size decreases by 5% before compression, but
26
after compression it is only 1.3%.
27
- There is no observable impact on raw compute speed, e.g., as
28
measured by our benchmarks module.
29
30
Conclusion: for now let's just use this on the kernel and nothing else.
31
The tradeoff is slightly increased build times and the potential of subtle
32
bugs being introduced in published modules. I don't know to what extent
33
I should trust binaryen's optimizer, but I don't blindly trust optimizers.
34
35
TODO: another thing to investigate is implementing setjmp/longjmp using
36
asyncify, which is part of cowasm-opt. For that I think we would asyncify
37
the kernel and whatever dynamic library actually needs this, and having
38
this script around will be useful.
39
"""
40
41
import os, shutil, stat, subprocess, sys
42
43
44
def run(cmd):
45
print(' '.join(cmd))
46
ret = subprocess.run(cmd)
47
if ret.returncode:
48
sys.exit(ret.returncode)
49
50
51
def wasm_opt(file_wasm, options):
52
# Run "wasm-opt --enable-mutable-globals -O ..." on the given wasm file, "in place"
53
try:
54
file_wasm_out = file_wasm + '.out'
55
run(['wasm-opt'] + options +
56
['--enable-mutable-globals', '-O', file_wasm, '-o', file_wasm_out])
57
st = os.stat(file_wasm)
58
shutil.move(file_wasm_out, file_wasm)
59
os.chmod(file_wasm, st.st_mode) # set permissions back
60
finally:
61
# ensure output file is cleaned up no matter what.
62
if os.path.exists(file_wasm_out):
63
try:
64
os.unlink(file_wasm_out)
65
except:
66
pass
67
68
69
def is_wasm_binary(target):
70
if not target.endswith('.so'):
71
st = os.stat(target)
72
if not (st.st_mode & stat.S_IXUSR):
73
return False
74
return open(target, "rb").read(4) == b'\x00asm'
75
76
77
def find_wasm_files(path):
78
# Find all wasm binaries in all subdirectories of path.
79
v = []
80
if not os.path.isdir(path):
81
if is_wasm_binary(path):
82
v.append(path)
83
return v
84
for (dirpath, dirnames, filenames) in os.walk(path):
85
for filename in filenames:
86
p = os.path.join(dirpath, filename)
87
if is_wasm_binary(p):
88
v.append(p)
89
return v
90
91
92
# make it easier to do in parallel.
93
94
95
def wasm_opt_all(targets, options):
96
for target in targets:
97
wasm_opt(target, options)
98
99
100
def wasm_opt_all_parallel(targets, options):
101
if len(targets) == 0:
102
return
103
import asyncio
104
loop = asyncio.new_event_loop()
105
106
def background(f):
107
108
def wrapped(*args, **kwargs):
109
return loop.run_in_executor(None, f, *args, **kwargs)
110
111
return wrapped
112
113
@background
114
def f(target):
115
return wasm_opt(target, options)
116
117
looper = asyncio.gather(*[f(target) for target in targets])
118
loop.run_until_complete(looper)
119
120
121
def main():
122
if len(sys.argv) <= 1:
123
print("Usage: %s [path] ..." % sys.argv[0])
124
print("Run wasm-opt on all wasm files in the path(s).")
125
sys.exit(1)
126
127
targets = []
128
options = []
129
for x in sys.argv[1:]:
130
if x.startswith('-'):
131
options.append(x)
132
else:
133
targets += find_wasm_files(x)
134
print(' '.join(targets))
135
wasm_opt_all_parallel(targets, options)
136
137
138
if __name__ == '__main__':
139
main()
140
141