文字内容
1. JavaScript in IoT Yorkie Rokid Engineer
4. ⾃自我介绍 • • • Creator of ShadowNode & tensorflow-nodejs Node.js Collaborator Rokid Engineer
5. ⽬目录 • What’s the IoT? • Why JavaScript? • Node.js on Edge Device • Make an OS for JavaScript Developer
6. What’s the IoT • Service-oriented user interface • Resource restricted device • Efficient is greater than elegance
7. Service-oriented User Interface • user interface is expanding to every IoT device. • user interface is adaptive, also known as AUI. • screen is optional. • collaboration everywhere. • in-parallel runtimes.
8. Resource Restricted Device low-end medium-end smart phone RAM / ROM 4MB / 4MB 128MB / 128MB 2GB / 16GB Processors 1 Core 1 - 4 Core 4 Core+ OS RTOS Linux Android / iOS
9. Resource Restricted Device 20% 15% 20% * 128MB = 25.6MB 25% Kernel DSP 40% System Applications
10. Efficient is greater than Elegance • trade off everyday. • efficient workflows on the hand. • efficiency is elegant at real world.
11. Why JavaScript • IoT fragmentation • JavaScript ecosystems (including Web and Node.js)
12. IoT Fragmentation
13. The unified Web — WOT
14. Popular Programming language — JavaScript
15. Popular Programming language — JavaScript “Any application that can be written in JavaScript, will eventually be written in JavaScript ?”
16. Node.js on Edge Device — ShadowNode • Brief introduction and history of ShadowNode • • Use N-API Optimizations on device
17. ShadowNode: brief introduction • bio: use Node.js in your end device • support Linux and macOS • support x86, arm and aarch64 • core APIs: assert / buffer / N-API Add-ons / child process / crypto / dns / events / file system / http / https / module / net / os / process / timers / TLS / UDP • • • extended APIs: WebSocket / D-Bus / MQTT / Profiler heap & cpu profiler a fork of another awesome project: IoT.js • • panda-project/jerryscript: memory optimized ECMAScript JITless VM panda-project/libtuv: memory optimized libuv fork
18. ShadowNode: project tree • src/ • deps/, the dependencies just like Node.js • src/modues.json, modules configure. • cmake/, the build scripts in CMake. • src/iotjs.c, bootstrap and entry file. • test/, the test directory • modules/, the C implementation of core modules. • testsets.json, defines the run set for unit tests. • js/, the JavaScript part of core modules. • napi-testsets.json, defines run set for N-API tests. • napi/, the N-API related files. • tools/ • tools/build.py, the build starter
19. ShadowNode: history • 2017.09 Migrate a Node.js application from android to linux which only owns 256MB RAM at Rokid. • 2017.10 Try to do reduction on this existed application includes: tree-shaking, unnecessary dependencies and refactors, but failed always. • 2017.11 The IoT.js project was found, but no TLS, MQTTs, D-Bus and child process, start hacking ShadowNode and supporting the above modules and some of NPM modules. • 2018.06 N-API implementation has been released. • 2018.08 Start building a new project which is based on ShadowNode.
20. Compare to Node.js NPM Node.js Official ShadowNode V8 JerryScript libuv libtuv OpenSSL mbedTLS
21. Compare to Node.js Node.js Resident set size 30 MB 22.5 MB ShadowNode 27.9 MB 19.5 MB 15 MB 7.5 MB 1.5 MB 0 MB macOS 2.2 MB aarch64
22. Compare to Node.js Node.js ShadowNode 0.5 s bootstrap time 0.43 s 0.38 s 0.25 s 0.13 s 0.12 s 0.05 s 0s macOS 0.05 s aarch64
23. Compare to Node.js • • • ShadowNode is not going to replace the official Node.js ShadowNode shall be the subset of Node.js ecosystem on embedded system ShadowNode embraces Node.js community
24. Use N-API • how it works seamlessly on Node.js & ShadowNode • implement HandleScope on JerryScript • try N-API • test N-API implementation
25. N-API ABI Stability • no re-compiling on different Node.js versions. • no re-compiling on different JavaScript engine like node-chakracore. • no re-compiling on the runtime outside of Node.js like ShadowNode.
26. Comments from Node.js Team
27. HandleScope on V8 void Init(Handle<Object> target) { HandleScope scope; target->Set(String::New(“gc"), FunctionTemplate::New(GC)->GetFunction()); target->Set(String::New(“pause”), FunctionTemplate::New(Pause)->GetFunction()); target->Set(String::New("resume"), FunctionTemplate::New(Resume)->GetFunction()); } // copy from https://github.com/bnoordhuis/node-profiler
28. HandleScope on JerryScript JS_FUNCTION(Test) { jerry_value_t name = JS_GET_ARG(0, string); // ... do something jerry_release_value(name); }
29. HandleScope on JerryScript JS_FUNCTION(Test) { jerry_value_t name = JS_GET_ARG(0, string); // ... do something jerry_release_value(name); } JS_FUNCTION(TestWithHandleScope) { jerryx_handle_scope scope; jerryx_open_handle_scope(&scope); jerry_value_t name = jerryx_create_handle(JS_GET_ARG(0, string)); jerryx_close_handle_scope(scope); // name has been released }
30. HandleScope on JerryScript
31. Usage threshold of HandleScope • HandleScope is nested, but it commonly is up to a threshold value. • introduce a pool to pre-allocated handle scopes could avoid most malloc() in real world. • the pool is configurable by JERRYX_SCOPE_PRELIST_SIZE(20), it stands for the maximum depth at call stack without malloc() at N-API add-on.
32. Try N-API
33. Test N-API implementation • pull N-API tests from nodejs/node repository. • build add-ons with node-gyp. • run tests on ShadowNode. • NAPI Test Suite reuses tests for different N-API implementations, but still work in progress.
34. Optimizations on Device • re-implement the NPM package in C is always working. • otherwise • introduce NODE_PRIORITIZED_PATH to decrease the module path searching on device case. • linux Copy-On-Write.
35. C/C++ advanced NPM package • JavaScript must be the application-level language at embedded device. • JavaScript object heap is always expensive than system allocators. • keep the API to be consistent at JavaScript. • some packages are optimized in this way: MQTT / WebSocket.
36. NODE_PRIORITIZED_PATH • NODE_PRIORITIZED_PATH=/usr/lib/node_modules node app.js • A workaround to improve performance of the Node.js module searching algorithm. • most `require` are from the global path, which differs from server-side use cases. • • no effect on previous algorithm by introducing the new env variable. optimized 30% on booting the main service.
37. Copy On Write • • • principle of laziness, do action when it’s really required. do copy when the data is really changed. linux fork() implements COW.
38. Copy On Write - uv_spawn function spawn (file, args) { var pid = fork() if (pid === 0) { execvp(file, args) // this disables COW // starting VM and load script } } spawn(‘test.js’, [])
39. Copy On Write - fork var fork = require(‘linux-sys’).fork // load common modules for children var player = require(‘player’) var http = require(‘http’) var foobar = require(‘foobar’) // start forking var pid = fork() if (pid == 0) { // here is the child process // use player / http / foobar } 4ms
40. Hive — Node.js process incubator Zygote Hive Register socket Register socket Preload all Java class Preload all Node.js core modules Preload resource Preload third-party node modules Listen for fork() connection Listen for fork() connection
41. Hive — Node.js process incubator
42. Hive — Node.js process incubator hive-fork nodejs-fork 57.8ms 60ms Bootstrap Time nodejs-spawn 47ms 45ms 59.4ms 42.7ms 30ms 15ms 8.1ms 7.7ms common require-lodash
43. Make an OS for JavaScript Developer • Guide to make an operating system • Principles of design API • Boundaries between JavaScript and others
44. Guide to make an operating system Source code Google Repo Kernel Linux Build Framework OpenWRT Configuration defconfig
45. Google Repo repo init -u https://github.com/yodaos-project/yodaos -m default.xml -b master repo sync
46. Google Repo <?xml version="1.0" encoding="UTF-8"?> <manifest> <remote name="github" fetch="https://github.com" review="https://github.com" /> <default revision="master" remote="github" sync-c="true" sync-j="2" /> <include name="manifests/base.xml" /> <project path="libs/nodejs" name="yodaos-project/ShadowNode" remote="github" revision=“master" /> <project path="products/yodaos/rbpi-3b-plus" name="yodaos-project/product-raspberry" remote="github" revision=“refs/tags/v1.0" /> </manifest>
47. Kernel — Why Linux? • provide the Posix-compatible APIs for system programming. • provide the richness drivers ecosystem for different hardwares. • microphone • speaker • input event • motion control • light & display • …
48. OpenWRT - build system for linux distribution
49. Makefile and defconfig # config target CONFIG_TARGET_leo=y CONFIG_TARGET_leo_k18_universal=y CONFIG_TARGET_leo_k18_universal_LEO_K18_UNIVERSAL=y # config product CONFIG_PRODUCT_OS_NAME="yodaos" CONFIG_PRODUCT_NAME="Rokid-Devkit" CONFIG_PRODUCT_PATHNAME="rokid/universal" # config kernel CONFIG_EXTERNAL_KERNEL_CONFIG=y CONFIG_EXTERNAL_KERNEL_TREE="$(TOPDIR)/../kernel/k18/4.4" # config packages CONFIG_PACKAGE_nodejs=y CONFIG_PACKAGE_wget=y CONFIG_PACKAGE_wpa-cli=y CONFIG_PACKAGE_wpa-supplicant=y CONFIG_PACKAGE_light=y
50. Makefile and defconfig $ cd ./openwrt $ cp ./path/to/your/defconfig .config $ make defconfig $ make -j32
51. There is an OS for Web community YODAOS
52. Principles of design API • consider the messaging ways firstly • the shape of applications on your OS • design your “system calls” • design your application API
53. Boundaries between JavaScript and others • JavaScript is application-level • outside of Node.js
54. Example: LED Architecture(0)
55. Example: LED Architecture module.exports = function render (light, args, done) { var muted = !!(args && args.muted) light.clear() if (!muted) { light.sound('system://mic_enable.ogg') light.render() done() } else { light.sound('system://mic_close_tts.ogg') light.fill(255, 0, 0) light.render() } }
56. Example: LED Architecture(1)
59. THANKS THANKS! THANKS!