Upgrade to Pro — share decks privately, control downloads, hide ads and more …

WebAssembly from the Java Perspective

WebAssembly from the Java Perspective

An introduction to WebAssembly and ho to build it from Java and Kotlin.

Dmitry Alexandrov

November 23, 2019
Tweet

More Decks by Dmitry Alexandrov

Other Decks in Programming

Transcript

  1. JavaScript: JavaScript (/ˈdʒɑːvəˌskrɪpt/), often abbreviated as JS, is a high-level,

    interpreted programming language that conforms to the ECMAScript specification. It is a language that is also characterized as dynamic, weakly typed, prototype-based and multi-paradigm. Created 04 December 1995 by Brendan Eich. © Wikipedia
  2. You will be surprised...: Compiling for the Web with WebAssembly

    (Google I/O '17) function add(a, b) { return a + b }
  3. JS!

  4. 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/Java/Python, enabling deployment on the web for client and server applications. © Wikipedia
  5. What is WebАssembly The kind of binary format being considered

    for WebAssembly can be natively decoded much faster than JavaScript can be parsed (experiments show more than 20× faster). On mobile, large compiled codes can easily take 20–40 seconds just to parse, so native decoding (especially when combined with other techniques like streaming for better-than-gzip compression) is critical to providing a good cold- load user experience. from the FAQ of WebAssembly
  6. What is WebAssembly Efficient and fast The Wasm stack machine

    is designed to be encoded in a size- and load-time- efficient binary format. WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms.
  7. What is WebAssembly Efficient and fast The Wasm stack machine

    is designed to be encoded in a size- and load-time- efficient binary format. WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms. Safe WebAssembly describes a memory-safe, sandboxed execution environmentthat may even be implemented inside existing JavaScript virtual machines. When embedded in the web, WebAssembly will enforce the same- origin and permissions security policies of the browser.
  8. What is WebAssembly Efficient and fast The Wasm stack machine

    is designed to be encoded in a size- and load-time- efficient binary format. WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms. Safe WebAssembly describes a memory-safe, sandboxed execution environment that may even be implemented inside existing JavaScript virtual machines. When embedded in the web, WebAssembly will enforce the same- origin and permissions security policies of the browser. Open and debuggable WebAssembly is designed to be pretty-printedin a textual format for debugging, testing, experimenting, optimizing, learning, teaching, and writing programs by hand. The textual format will be used when viewing the source of Wasm modules on the web.
  9. What is WebAssembly Efficient and fast The Wasm stack machine

    is designed to be encoded in a size- and load-time- efficient binary format. WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms. Safe WebAssembly describes a memory-safe, sandboxed execution environment that may even be implemented inside existing JavaScript virtual machines. When embedded in the web, WebAssembly will enforce the same- origin and permissions security policies of the browser. Open and debuggable WebAssembly is designed to be pretty-printed in a textual format for debugging, testing, experimenting, optimizing, learning, teaching, and writing programs by hand. The textual format will be used when viewing the source of Wasm modules on the web. Part of the open web platform WebAssembly is designed to maintain the versionless, feature-tested, and backwards-compatible nature of the web. WebAssembly modules will be able to call into and out of the JavaScript context and access browser functionality through the same Web APIs accessible from JavaScript. WebAssembly also supports non-webembeddings.
  10. Its very very fresh • April 2015 - WebAssembly Community

    Group started • June 2015 - The first public announcement • March 2016 - Definition of core feature with multiple interoperable implementations • October 2016 - Browser Preview announced with multiple interoperable implementations • February 2017 - Official logo chosen • March 2017 - Cross-browser consensus and end of Browser Preview
  11. In the greater scheme of things, the arrival of WebAssembly

    means that you will not be forced anymore to use JavaScript for the web, because it is the only thing that run in the browser!
  12. The founding principle ... is to integrate well with the

    existing JavaScript world. This ranges from technical things, such as interoperability and sharing the security policies (same-origin),
  13. The founding principle ... is to integrate well with the

    existing JavaScript world. This ranges from technical things, such as interoperability and sharing the security policies (same-origin), to tooling integration, such as as supporting the View Source functionality of web browsers.
  14. Why C?? • Objective of the initial release (MVP) •

    MVP is not Most Valuable Professional
  15. Why C?? • Objective of the initial release (MVP) •

    MVP is not Most Valuable Professional • MVP is Minimal Viable Product
  16. Why C?? • Objective of the initial release (MVP) •

    MVP is not Most Valuable Professional • MVP is Minimal Viable Product • The MVP of WebAssembly does not support garbage collection
  17. Why C?? • Objective of the initial release (MVP) •

    MVP is not Most Valuable Professional • MVP is Minimal Viable Product • The MVP of WebAssembly does not support garbage collection • Rely on LLVM (one of the most used set of compiler tools)
  18. MVP Feature Tracking issue Status Phase Specification 1077 in progress

    Proposed spec text available Threads 1073 in progress Feature proposal Fixed-width SIMD 1075 in progress Feature proposal Exception handling 1078 in progress Feature proposal Garbage collection 1079 in progress Feature proposal Bulk memory operations 1114 in progress Feature proposal Web Content Security Policy 1122 in progress Pre-proposal ECMAScript module integration 1087 in progress Feature proposal Tail Call 1144 in progress Feature proposal Non-trapping float-to-int conversions 1143 in progress Implementation phase Multi-value 1146 in progress Implementation phase Host bindings 1148 in progress Feature proposal Sign-extension operators 1178 in progress Implementation phase Import/Export Mutable Globals 1179 in progress Implementation phase Type Reflection for WebAssembly JavaScript API 1181 in progress Feature proposal Unmanaged closures 1182 in progress Feature proposal JavaScript BigInt to WebAssembly i64 integration 1186 in progress Proposed Spec Text Available Custom Annotation Syntax in the Text Format 1192 in progress Feature proposal
  19. External calls: step 1 – the code (C) extern int

    print_value(int x); void call_func(int input) { print_value(input * 3); }
  20. External calls: step 2 – interop (JS) if ('WebAssembly' in

    window) { var importObj = { env: { _print_value: x => document.getElementById('wasm').innerHTML = 'Value:' + x } }; WebAssembly.instantiateStreaming(fetch('call_func.wasm'), importObj).then(result => result.instance.exports._call_func(9)); }
  21. External calls: step 3 – all together (HTML) <html> <head>

    <meta charset='UTF-8'> <script src="output.js"></script> </head> <body> <label id='wasm'></label> </body> </html>
  22. Since we are low level • Only binary data can

    be moved • The data is moved in chunks called pages
  23. Since we are low level • Only binary data can

    be moved • The data is moved in chunks called pages • Each page is 64K
  24. Since we are low level • Only binary data can

    be moved • The data is moved in chunks called pages • Each page is 64K • Memory access is done via offsets
  25. Moving data: step 1 – the code (C) #define NUM_VALS

    8 int input_array[NUM_VALS]; int scalar_add() { int product = 0; for(int i=0; i<NUM_VALS; i++) { product += input_array[i]; } return product; } int* getInputArrayOffset() { return &input_array[0]; }
  26. Moving data: step 2 – interop (JS) if ('WebAssembly' in

    window) { // Add memory object to the import object var memObj = new WebAssembly.Memory({initial: 256, maximum: 256}); var importObj = { env: { memory: memObj} }; // Set the import object in instantiateStreaming WebAssembly.instantiateStreaming(fetch('scalarAdd.wasm'), importObj) .then(result => { // Get the addresses of the input array offset = result.instance.exports._getInputArrayOffset(); // Create and initialize array var inputArray = new Uint32Array(memObj.buffer, offset, 8); inputArray.fill(6); // Compute the product product = result.instance.exports._scalar_add(); document.getElementById('wasm').innerHTML = 'Scalar add product: ' + product; }); }
  27. Moving data: step 2 – interop (JS) if ('WebAssembly' in

    window) { // Add memory object to the import object var memObj = new WebAssembly.Memory({initial: 256, maximum: 256}); var importObj = { env: { memory: memObj} }; // Set the import object in instantiateStreaming WebAssembly.instantiateStreaming(fetch('scalarAdd.wasm'), importObj) .then(result => { // Get the addresses of the input array offset = result.instance.exports._getInputArrayOffset(); // Create and initialize array var inputArray = new Uint32Array(memObj.buffer, offset, 8); inputArray.fill(6); // Compute the product product = result.instance.exports._scalar_add(); document.getElementById('wasm').innerHTML = 'Scalar add product: ' + product; }); }
  28. Moving data: step 2 – interop (JS) if ('WebAssembly' in

    window) { // Add memory object to the import object var memObj = new WebAssembly.Memory({initial: 256, maximum: 256}); var importObj = { env: { memory: memObj} }; // Set the import object in instantiateStreaming WebAssembly.instantiateStreaming(fetch('scalarAdd.wasm'), importObj) .then(result => { // Get the addresses of the input array offset = result.instance.exports._getInputArrayOffset(); // Create and initialize array var inputArray = new Uint32Array(memObj.buffer, offset, 8); inputArray.fill(6); // Compute the product product = result.instance.exports._scalar_add(); document.getElementById('wasm').innerHTML = 'Scalar add product: ' + product; }); }
  29. Moving data: step 2 – interop (JS) if ('WebAssembly' in

    window) { // Add memory object to the import object var memObj = new WebAssembly.Memory({initial: 256, maximum: 256}); var importObj = { env: { memory: memObj} }; // Set the import object in instantiateStreaming WebAssembly.instantiateStreaming(fetch('scalarAdd.wasm'), importObj) .then(result => { // Get the addresses of the input array offset = result.instance.exports._getInputArrayOffset(); // Create and initialize array var inputArray = new Uint32Array(memObj.buffer, offset, 8); inputArray.fill(6); // Compute the product product = result.instance.exports._scalar_add(); document.getElementById('wasm').innerHTML = 'Scalar add product: ' + product; }); }
  30. Moving data: step 2 – interop (JS) if ('WebAssembly' in

    window) { // Add memory object to the import object var memObj = new WebAssembly.Memory({initial: 256, maximum: 256}); var importObj = { env: { memory: memObj} }; // Set the import object in instantiateStreaming WebAssembly.instantiateStreaming(fetch('scalarAdd.wasm'), importObj) .then(result => { // Get the addresses of the input array offset = result.instance.exports._getInputArrayOffset(); // Create and initialize array var inputArray = new Uint32Array(memObj.buffer, offset, 8); inputArray.fill(6); // Compute the product product = result.instance.exports._scalar_add(); document.getElementById('wasm').innerHTML = 'Scalar add product: ' + product; }); }
  31. Moving data: step 2 – interop (JS) if ('WebAssembly' in

    window) { // Add memory object to the import object var memObj = new WebAssembly.Memory({initial: 256, maximum: 256}); var importObj = { env: { memory: memObj} }; // Set the import object in instantiateStreaming WebAssembly.instantiateStreaming(fetch('scalarAdd.wasm'), importObj) .then(result => { // Get the addresses of the input array offset = result.instance.exports._getInputArrayOffset(); // Create and initialize array var inputArray = new Uint32Array(memObj.buffer, offset, 8); inputArray.fill(6); // Compute the product product = result.instance.exports._scalar_add(); document.getElementById('wasm').innerHTML = 'Scalar add product: ' + product; }); }
  32. Moving data: step 3 – all together (HTML) <html> <head>

    <meta charset='UTF-8'> <script src="output.js"></script> </head> <body> <label id='wasm'></label> </body> </html>
  33. ... there are also other options: • -g4 and –source-map-base

    http://localhost:8080/ • emcc add.c -O1 -g4 -s WASM=1 -s SIDE_MODULE=1 -s EXPORTED_FUNCTIONS='["_add"]' -o add.wasm --source-map-base http://localhost:8080/ *http://webassemblycode.com/using-browsers-debug-webassembly/
  34. JWebAssembly: Java public class Add { @Export public static int

    add( int a, int b ) { return a + b; } }
  35. JWebAssembly: Java public static void main(String[] args) throws Exception {

    JWebAssembly webAsm = new JWebAssembly(); webAsm.addFile(new File(”<..>/Add.class")); String text = webAsm.compileToText(); System.out.println(text); }
  36. TeaVM: Step 1 – setup (Maven) ... <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-classlib</artifactId>

    <version>${project.version}</version> </dependency> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-jso-apis</artifactId> <version>${project.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.teavm</groupId> <artifactId>teavm-interop</artifactId> <version>${project.version}</version> </dependency> ...
  37. TeaVM: Step 2 – the code (Java) public class WasmFunctions

    { @Export(name = "thePurposeOfLife") public static int getThePurposeOfLife(){ return 43; } public static void main(String[] args) { } }
  38. TeaVM: Step 3 – compile to Wasm (Maven) <plugin> <artifactId>maven-war-plugin</artifactId>

    <version>2.4</version> <configuration> <webResources> <resource> <directory>${project.build.directory}/generated/wasm</directory> </resource> </webResources> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin>
  39. TeaVM: Step 4 – compile to Wasm (Maven) <plugin> <groupId>org.teavm</groupId>

    <artifactId>teavm-maven-plugin</artifactId> <version>${project.version}</version> <executions> <execution> <id>wasm-client</id> <goals> <goal>compile</goal> </goals> <configuration> <targetDirectory>${project.build.directory}/generated/wasm/wasm</targetDirectory> <mainClass>local.mitia.wasm.WasmFunctions</mainClass> <debugInformationGenerated>true</debugInformationGenerated> <targetType>WEBASSEMBLY</targetType> <optimizationLevel>FULL</optimizationLevel> <heapSize>8</heapSize> </configuration> </execution> </executions> </plugin>
  40. TeaVM: Step 5 – all together (JS/HTML) JS: if ('WebAssembly'

    in window) { // Set the import object in instantiateStreaming WebAssembly.instantiateStreaming(fetch('wasm /classes.wasm')) .then(result => { //the Purpse of Life is: result = result.instance.exports.thePurposeOfLife(); document.getElementById('wasm').innerHTML = 'The Purpose of life according to teavm wasm from java: ' + result; }); } HTML: <html> <head> <meta charset='UTF-8'> <script src="output.js"></script> </head> <body> <label id='wasm'></label> </body> </html>
  41. s

  42. Kotlin way: step 1 – the code (kt) import kotlinx.interop.wasm.dom.*

    import kotlinx.wasm.jsinterop.* fun main(args: Array<String>) { println("Hello Kotlin/Native!") }
  43. Kotlin way: step 2 – the build (gradle) buildscript {

    repositories { jcenter() maven { url "http://kotlin.bintray.com/kotlinx" } maven { url "https://plugins.gradle.org/m2/" } maven { url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies" } } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.moowork.gradle:gradle-node-plugin:$gradle_node_version" classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:$kotlin_native_version" } } apply plugin: 'konan' konanArtifacts { program('wasmFunctions', targets: ['wasm32']) { srcDir 'src/main/kotlin' libraries { useRepo 'lib' klib 'dom' } } }
  44. Kotlin way: step 2 – the build (gradle) buildscript {

    repositories { jcenter() maven { url "http://kotlin.bintray.com/kotlinx" } maven { url "https://plugins.gradle.org/m2/" } maven { url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies" } } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.moowork.gradle:gradle-node-plugin:$gradle_node_version" classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:$kotlin_native_version" } } apply plugin: 'konan' konanArtifacts { program('wasmFunctions', targets: ['wasm32']) { srcDir 'src/main/kotlin' libraries { useRepo 'lib' klib 'dom' } } }
  45. Real world: 0 500 1000 1500 2000 2500 JS Asm.js

    Wasm Sorting 10k entries Sorting 10k entries