Path: blob/main/core/wasm-opt/src/cowasm_opt.py
1396 views
#!/usr/bin/env python31"""2Use this script to run wasm-opt on all the wasm binaries in a directory tree.3This may take a while, but is useful to do before publication, in order to4reduce the size of wasm files and possibly make them faster.56USAGE:78cowasm-opt [-options to wasm-opt, e.g., --asyncify] [path] ...910It will walk the path's and identify all the wasm binaries, where a file is11considered a wasm executable if:1213- it is executable or ends with .so (python extension module), and14- the file contents start with b'\x00asm'1516In particular, we do not consider the filename extension.1718OBSERVATIONS:1920- For our static kernel, wasm-opt decreases the size by 11%.21- For most of our dynamic libraries (e.g., python extension modules),22wasm-opt decreases the size by less than 3%.23For numpy it actually makes it larger (?).24- For python.wasm the size decreases by 5% before compression, but25after compression it is only 1.3%.26- There is no observable impact on raw compute speed, e.g., as27measured by our benchmarks module.2829Conclusion: for now let's just use this on the kernel and nothing else.30The tradeoff is slightly increased build times and the potential of subtle31bugs being introduced in published modules. I don't know to what extent32I should trust binaryen's optimizer, but I don't blindly trust optimizers.3334TODO: another thing to investigate is implementing setjmp/longjmp using35asyncify, which is part of cowasm-opt. For that I think we would asyncify36the kernel and whatever dynamic library actually needs this, and having37this script around will be useful.38"""3940import os, shutil, stat, subprocess, sys414243def run(cmd):44print(' '.join(cmd))45ret = subprocess.run(cmd)46if ret.returncode:47sys.exit(ret.returncode)484950def wasm_opt(file_wasm, options):51# Run "wasm-opt --enable-mutable-globals -O ..." on the given wasm file, "in place"52try:53file_wasm_out = file_wasm + '.out'54run(['wasm-opt'] + options +55['--enable-mutable-globals', '-O', file_wasm, '-o', file_wasm_out])56st = os.stat(file_wasm)57shutil.move(file_wasm_out, file_wasm)58os.chmod(file_wasm, st.st_mode) # set permissions back59finally:60# ensure output file is cleaned up no matter what.61if os.path.exists(file_wasm_out):62try:63os.unlink(file_wasm_out)64except:65pass666768def is_wasm_binary(target):69if not target.endswith('.so'):70st = os.stat(target)71if not (st.st_mode & stat.S_IXUSR):72return False73return open(target, "rb").read(4) == b'\x00asm'747576def find_wasm_files(path):77# Find all wasm binaries in all subdirectories of path.78v = []79if not os.path.isdir(path):80if is_wasm_binary(path):81v.append(path)82return v83for (dirpath, dirnames, filenames) in os.walk(path):84for filename in filenames:85p = os.path.join(dirpath, filename)86if is_wasm_binary(p):87v.append(p)88return v899091# make it easier to do in parallel.929394def wasm_opt_all(targets, options):95for target in targets:96wasm_opt(target, options)979899def wasm_opt_all_parallel(targets, options):100if len(targets) == 0:101return102import asyncio103loop = asyncio.new_event_loop()104105def background(f):106107def wrapped(*args, **kwargs):108return loop.run_in_executor(None, f, *args, **kwargs)109110return wrapped111112@background113def f(target):114return wasm_opt(target, options)115116looper = asyncio.gather(*[f(target) for target in targets])117loop.run_until_complete(looper)118119120def main():121if len(sys.argv) <= 1:122print("Usage: %s [path] ..." % sys.argv[0])123print("Run wasm-opt on all wasm files in the path(s).")124sys.exit(1)125126targets = []127options = []128for x in sys.argv[1:]:129if x.startswith('-'):130options.append(x)131else:132targets += find_wasm_files(x)133print(' '.join(targets))134wasm_opt_all_parallel(targets, options)135136137if __name__ == '__main__':138main()139140141