Asyncify optimizations #20
Replies: 1 comment 1 reply
-
This worked. It's a little tricky because there are indeed some additional indirect calls (e.g. in The resulting .wasm file is 1,474,323 bytes, down from 1,858,934 bytes. The build without Asyncify is 1,089,707 bytes, so this halves the Asyncify size penalty, from 70.6% to 35.3%. That's a fairly significant savings. I have not attempted to measure the performance gain. I suspect that will be marginal because the major costs are likely in unwinding/rewinding the stack when actually executing an asynchronous function. I'm leaving this in a feature branch for now. The tests pass but they aren't comprehensive enough to be confident that the generated Asyncify configuration isn't missing more indirect calls. |
Beta Was this translation helpful? Give feedback.
-
Asyncify adds both space and time overhead to an asynchronous SQLite build. Out of the ~50 C API
sqlite3_*
calls that wa-sqlite uses, only these are actually being made asynchronously:sqlite3_open_v2
sqlite3_close
sqlite3_prepare_v2
sqlite3_reset
sqlite3_step
sqlite3_finalize
By default Asyncify conservatively modifies any function that might be in a call chain that ends up calling an asynchronous Javascript function. This includes any function that makes an indirect call via a function pointer, and SQLite has a lot of them. Setting the Emscripten
-s ASYNCIFY_ADVISE
flag tells us what functions are being selected, and there are over 700 of them in wa-sqlite., Many of them are obviously always synchronous, e.g.sqlite3_malloc
andsqlite3_free
, and so this is an opportunity to reclaim size and performance.Emscripten provides a
-s ASYNCIFY_IGNORE_INDIRECT
switch but using that means that real indirect asynchronous call chains (and all of them are indirect) need to be manually added with-s ASYNCIFY_ADD
. Fortunately it looks like all the indirect calls to Javascript are made via wrapper functions, e.g. calls to VFSxOpen
implementations are made via the internal functionsqlite3OsOpen
. Static analysis can be used to determine functions that can reach the wrapper function.Emscripten itself won't do this static analysis, even though it does do it for
ASYNCIFY_IMPORTS
, but we can use clang to generate the call graph:clang -S -emit-llvm [compilation flags] sqlite3.c -o - | opt -analyze -print-callgraph
If we know all the wrapper functions then we can process the output of the above command to configure
ASYNCIFY_ADD
. This should work provided that the asynchronous functions are always called via identified wrappers and there aren't other indirect calls leading to functions that call the wrappers.Beta Was this translation helpful? Give feedback.
All reactions