Kevin Cheung AutoCAD & WebAssembly Moving a 30 Year Code Base to the Web(英语演讲)

1. AutoCAD on WebAssembly Accelerate Startup Timing
4. Outline • History and Current Status • Basics of WebAssembly • Lessons from the Wild • AutoCAD’s Startup Sequence • Improve Startup Performance • Future Development
5. History Rewrite from scratch Written in Flash 2009 C++ -> Java -> JavaScript using Tangible and then Google Web Toolkit 2013 Cross-compiling from original AutoCAD source code Asm.js using Emscripten Wasm using Emscripten and Binaryen 2015 2017 AutoCAD released on Web!!! 2018
6. AutoCAD 2019 • Multi-browser support • Faster feature rollout
7. What is WebAssembly? WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.
8. What is WebAssembly? • Virtual • Runs on a virtual machine • Compilation target, intermediate representation, can’t code in it • Notable examples include Java bytecode, LLVM bitcode and C# CIL • Instruction Set Architecture • Instructions e.g. load, store, call • Linear memory model, data types e.g. i32, i64
9. What is WebAssembly? • Stack Machine • Instructions that are executed in order • Instructions manipulate values on a stack • 2 types of instructions • Pop and push values on the stack • Control instructions e.g. loop, if/else
10. Stack Machine In Action C++ int add42(int num) { return num + 42; } Binary Wast (Text) Stack 20 00 get_local 0 0 42 41 2a i32.const 42 42 6a I32.add
11. WebAssembly vs Asm.js • Faster loading time • Smaller payload • No Javascript parsing • Support from all major browsers • Standardization
12. Lessons From the Wild • Users do not have powerful machines!!! • Startup time is slow • Compilation takes ~25s when first released! • Affecting file open success • Runtime performance is slow as well • Sluggish on the more complex drawings
13. Startup Network Trace
14. Improve Startup Performance • Compile Wasm earlier • Baseline Compilation • Dynamic Linking
15. AutoCAD Startup Sequence Main Thread Web Worker UI Instantiation Wasm Instantiation Service Worker Instantiation Download Assets Start Web Worker C++ Startup Code 90% 10%
16. AutoCAD Start Up Main Thread UI Instantiation Wasm Compilation Web Worker Download Assets Service Worker Instantiation Start Web Worker Post Module C++ Startup Code
17. Baseline Compilation • Less optimized code is compiled faster • Optimized code compiles in the background • Reduced Startup time from 25s to ~10s • Slower performance before optimized code finish compilation
18. Dynamic Linking on the Web • Move ‘cold’ code to a separate module • Speed up build time • Slightly different from native desktop
19. Emscripten’s Dynamic Linking Framework Javascript System Libraries Main Module Application Code LLVM Bitcode System Libraries Side Module Application Code
20. Side Module Loading Mechanism • Startup • dlopen • Delay loading
21. Side Module Loading Mechanism Impact on Startup performance Code changes required? Startup dlopen Delay loading Yes No No Javascript Javascript and C++ Javascript
22. WebAssembly Dynamic Linking export start import global memory data table element global memory function export import start global memory data table element function code function code Main Module Side Module
23. Side Wasm Main Wasm (func $_main (type $t5) (param $p0 i32) (param $p1 i32) (result i32) . . . get_local $p0 get_local $p1 call $_sidey i32.const 0 ) (func $_sidey (type $t0) (param $p0 i32) (param $p1 i32) (local $l0 i32) (local $l1 Javascript Stub i32) (local $l2 i32) (local $l3 i32) function _sidey() { . return . Module['_sidey'].apply(null, . arguments); if $I0 } get_local $p0 get_local $p1 call $_mainy get_local $l0 set_global $g1 )
24. Limitations of Dynamic Linking • Main module not aware of syslibs needed by side module • Size too big if we keep everything • Better solution is to: • Determine imports statically • Specify the imports to the main module • Build the main module
25. JavaScript Stubs are Bad • Bloats up total file size • Wasm -> JS -> Wasm transitions are slow • i64 wasm to Javascript 32bit Number incompatibility • Inconsistent function addresses due to JS stubs
26. Wasm Stub Code (func $legalstub$__Z10fnLongLongx (type $t4) (param $p0 i32) (param $p1 i32) (result i32) (local $l0 i64) get_local $p0 i64.extend_u/i32 get_local $p1 i64.extend_u/i32 i64.const 32 i64.shl i64.or call $__Z10fnLongLongx set_local $l0 get_local $l0 i64.const 32 i64.shr_u i32.wrap/i64 call $setTempRet0 get_local $l0 i32.wrap/i64) Wasm Original Function (func $__Z10fnLongLongx (type $t35) (param $p0 i64) (result i64) (local $l0 i32) (local $l1 i32) get_global $g9 set_local $l0 get_global $g9 i32.const 16 i32.add set_global $g9 get_global $g9 get_global $g10 . . . get_local $l0 set_global $g9 get_local $p0 i64.const 9 i64.add)
27. Inconsistent Function Addresses main.cpp #include <stdio.h> int side(int arg); Address of side is different bool side2(); in both modules!!! int main1(int input) { printf("side2: ret:%d\n", side2()); printf("main1: side:%p\n", &side); return input; } int main(int argc, char *argv[]) { main1(input); return 0; } side.cpp #include <stdio.h> bool side2() { printf("side2: side:%p\n", &side); return true; } static int side(int arg) { printf("side.cpp side fn:%d\n", arg); return arg; }
28. Proposal to Remove JS Stubs • Exchange the function address and not the function itself • Pad the missing functions with a helper function • Replace missing functions in the table with the actual function • Replace bridge calls into call_indirect
29. Exception Handling • Delegated to Javascript • More Javascript stubs • Slower performance
30. exception.cpp #include <stdio.h> #include <emscripten.h> EMSCRIPTEN_KEEPALIVE int exceptionFn(int input) { if (input == 0) { throw 1; } else { return input; } } int main(int argc, char* argv[]) { try { exceptionFn(argc); } catch (...) { printf("Exception caught!!!\n"); } }
31. Exceptions Enabled (func $_main (type 7) (param i32 i32) (result i32) (local i32 i32) i32.const 0 set_global 3 i32.const 2 get_local 0 call $invoke_ii drop block (result i32) ;; label = @1 get_global 3 set_local 2 i32.const 0 set_global 3 get_local 2 end i32.const 1 i32.and if ;; label = @1 block (result i32) ;; label = @2 i32.const 0 call $___cxa_find_matching_catch_3 set_local 3 call $getTempRet0 drop get_local 3 end call $___cxa_begin_catch drop i32.const 2096 call $_puts call $___cxa_end_catch end i32.const 0) vs Exceptions Disabled (func $_main (type 7) (param i32 i32) (result i32) get_local 0 call $__Z11exceptionFni drop i32.const 0)
32. Javascript invoke_ii(index, a1) { var sp = stackSave(); try { return dynCall_ii(index, a1); } catch (e) { stackRestore(sp); if (e !== e + 0 && e !== "longjmp") throw e; _setThrew(1, 0); } Wasm } (func $dynCall_ii (type 7) (param i32 i32) (result i32) get_local 1 get_local 0 i32.const 3 i32.and call_indirect (type 4))
33. Moving Forward • • Disable Exceptions altogether • Enable them selectively
35. Future Development • LLVM Wasm Backend • SIMD
38. Q&A