WebAssembly 入门教程 c++、go、python编译wasm

打印 上一主题 下一主题

主题 867|帖子 867|积分 2601

WebAssembly 入门

了解 wasm 使用场景,复杂对象传递和履历法则。
简介

WebAssembly 是一种新的编码方式,可以在现代的网络欣赏器中运行。它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C/C ++ 等语言提供一个编译目的,以便它们可以在 Web 上运行。它也被筹划为可以与 JavaScript 共存,允许两者一起工作。
   WebAssembly 提供了一条途径,使得以各种语言编写的代码都可以以接近原生的速率在 Web 中运行。
  WebAssembly 筹划初衷


  • 它筹划的目的不是为了手写代码而是为了诸如 C、C++ 和 Rust 等低级源语言提供一个高效的编译目的。
  • WebAssembly 的模块可以被导入的到一个网络 app(或 Node.js)中,而且袒露出供 JavaScript 使用的 WebAssembly 函数。JavaScript 框架不但可以使用 WebAssembly 获得巨大性能上风和新特性,而且还能使得各种功能保持对网络开辟者的易用性。
怎样得到 WebAssembly 二进制文件


  • 现代语言险些都支持将 wasm 作为它的编译输出,如 Go、Python、C/C++、Rust、TypeScript 等都可以,只是由于 wasm 因为需要通过网络流传,因此大小很重要,因此更推荐如 C/C++、Rust 没有像垃圾网络器那样额外的运行时语言,可以使 wasm 体积更小。
  • 直接编写 wasm 代码(了解即可)

    • wasm 的二进制格式也有文本表示,两者之间 1:1 对应。你可以手工书写或者生成这种格式然后使用工具把它转换为二进制格式。这是一种用来在文本编辑器、欣赏器开辟者工具等工具中显示的中间形式
    • 二进制格式通常为 .wasm 格式,文本格式通常为 .wat 格式
    • 理解 WebAssembly 文本格式

WebAssembly 上风


  • 紧凑的二进制格式,使其可以或许以接近原生性能的速率运行,并支持在各种上下文中使用
  • 为诸如 C++ 和 Rust 等拥有低级的内存模型语言提供了一个编译目的以便它们可以或许在网络上运行
WebAssembly 劣势


  • 对于编写网络应用程序而言,不如 JavaScript 机动且富有表达力
  • 只有很小的一个值类型聚集,基本上限制在简单数值的范围,复杂数据类型需要进行编解码,如字符串、对象、数组需要先编码成二进制再存放到 wasm 内存段里
  • 与 JavaScript 胶水代码的交互带来的性能消耗肯定水平上抵消了 wasm 本身带来的性能提升
使用场景

WebAssembly = NaCl + asm.js
随着技能的发展,Mozilla 和 Google 的工程师出现了很多次的交流和合作,通过汲取 NaCl 和 asm.js 两者的优点,两边推出了一种全新的技能方案:


  • 和 NaCl/PNaCl 一样,基于二进制格式,从而可以或许被快速解析,到达原生代码的运行速率;
  • 和 PNaCl 一样,依赖于通用的 LLVM IR,如许既具备可移植性,又便于其他语言快速接入;
  • 和 asm.js 一样,使用 Emscripten 等工具链进行编译;另外,Emscripten 同时支持生成 asm.js 和二进制格式,当欣赏器不兼容新的二进制格式时,asm.js 可以作为降级方案;
  • 和 asm.js 一样,必须以非常自然的方式直接操作 Web API,而不消像 PNaCl 一样需要处理惩罚与 JavaScript 之间的通信;
这个技能方案在 2015 年正式定名为 WebAssembly,2017 年各大欣赏器生产商纷纷宣布支持 WebAssembly,2019 年 WebAssembly 正式成为 W3C 尺度,一场关于欣赏器的性能革命已经悄然展开。


使用 WebAssembly 的缘故原由


  • 关注性能敏感代码:使用 Rust 你不需要成为 JS 优化专家,不需要熟悉 JIT 内部实现,不需要魔法也能加快。
  • 集成方便:直接编译为 .wasm,使得现有的 JS 代码库可以增量式部分采用 WebAssembly。而且还可以保持你现有代码库,不需要重写。
  • 复用已有的其他语言编写的代码模块
开辟软件时使用 wasm 的常见方式


  • 纯 wasm 实现,包罗 ui 和逻辑
  • UI 使用 HTML/CSS/JS,逻辑计算使用 wasm
  • 复用其他语言的库,使用 wasm 移植到已有的 web 软件中
现有的使用 wasm 编写的应用有


  • Google Earth
  • AutoCAD Web
  • PhotoShop Web:Web 端和 PC 端由一份编码编译生成
  • Figma:wasm+rust 的 web 应用框架 zaplib
  • bilibili:wasm 版的 FFmpeg/tensorflow
WebAssembly 入门示例

从上面的学习中我们知道,WebAssembly 是一种通用的编码格式,而且已经有很多编程语言支持将源码编译成这种格式了,官方的 Getting Started 有一个详细的列表。这一节我们就跟着官方的教程实践一下下面这三种语言:


  • C/C++
  • Rust
  • Go
将 C/C++ 程序编译成 WebAssembly

设置环境

前提条件
  1. 需要安装CMake,VS,python2.7及以上
复制代码
拉取emsdk代码
  1. # Get the emsdk repo
  2. git clone https://github.com/emscripten-core/emsdk.git
  3. # Enter that directory
  4. cd emsdk
复制代码
安装并激活Emscripten
  1. emsdk.bat install latest
  2. emsdk.bat activate latest
  3. 在cmd窗口运行如上,有时需要./
复制代码
设置环境变量
  1. emsdk_evn.bat
复制代码
要留意的是:每次执行emcc前都要执行改下令,这是因为这个环境变量设置并不是全局的,如果使用emsdk.bat activate latest --global可以将下令更改为体系的环境变量,如许以后就不消再做环境变量的设置,但是他指向了Emscripten内置的Node.js,Python,java,如果体系中另有其他版本就大概产生冲突
安装完成后通过下面的下令检查环境是否正常:
  1. emcc --check
  2. emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.59 (0e4c5994eb5b8defd38367a416d0703fd506ad81)
  3. shared:INFO: (Emscripten: Running sanity checks)
复制代码
环境准备停当后,我们就可以将 C/C++ 的代码编译为 WebAssembly 了。写一个简单的 Hello World 程序 hello.c:
  1. #include <stdio.h>
  2. int main() {
  3.    printf("Hello World\n");
  4.    return 0;
  5. }
复制代码
然后使用 emcc 进行编译:
  1. emcc hello.c -o hello.html
复制代码
上面这个下令会生成三个文件:


  • hello.wasm - 这就是生成的 WebAssembly 二进制字节码文件
  • hello.js - 包含一段胶水代码(glue code)通过 JavaScript 来调用 WebAssembly 文件
  • hello.html - 方便开辟调试,在页面上显示 WebAssembly 的调用结果

我们不能直接用欣赏器打开 hello.html 文件,因为欣赏器不支持 file:// 形式的 XHR 请求,所以在 HTML 中无法加载 .wasm 等干系的文件,为了看到结果,我们需要一个 Web Server,好比 Nginx、Tomcat 等,不外这些安装和设置都比较麻烦,我们另有很多其他的方法快速启动一个 Web Server。
好比通过 npm 启动一个当地 Web Server:
  1. $ npx serve .
复制代码
或者使用 Python3 的 http.server 模块:
  1. $ python3 -m http.server
复制代码
访问 hello.html 页面如下:

可以看到我们在 C 语言中打印的 Hello World 乐成输出到欣赏器了。
另外,我们也可以将 C 语言中的函数袒露出来给 JavaScript 调用。默认情况下,Emscripten 生成的代码只会调用 main() 函数,其他函数忽略。我们可以使用 emscripten.h 中的 EMSCRIPTEN_KEEPALIVE 来袒露函数,新建一个 greet.c 文件如下:
  1. #include <stdio.h>
  2. #include <emscripten/emscripten.h>
  3. int main() {
  4.    printf("Hello World\n");
  5.    return 0;
  6. }
  7. #ifdef __cplusplus
  8. #define EXTERN extern "C"
  9. #else
  10. #define EXTERN
  11. #endif
  12. EXTERN EMSCRIPTEN_KEEPALIVE void greet(char* name) {
  13.    printf("Hello, %s!\n", name);
  14. }
  15. // Fibonacci function
  16. EXTERN int EMSCRIPTEN_KEEPALIVE fibonacci(int n) {
  17.    if (n <= 1) {
  18.        return n;
  19.    } else {
  20.        return fibonacci(n - 1) + fibonacci(n - 2);
  21.    }
  22. }
复制代码
上面的代码定义了一个 void greet(char* name) 函数,为了让这个函数可以在 JavaScript 中调用,编译时还需要指定 NO_EXIT_RUNTIME 和 EXPORTED_RUNTIME_METHODS 参数,将 ccall 导出来:
  1. emcc -o greet.html greet.c -s NO_EXIT_RUNTIME=1 -s EXPORTED_RUNTIME_METHODS=ccall
复制代码
greet.html 文件和上面的 hello.html 险些是一样的,我们在该文件中加几行代码来测试我们的 greet() 函数,起首加一个按钮:
  1. <input type="text" id="myinput" value=10>
  2. <button id="mybutton">Click me!</button>
复制代码
然后为它添加点击变乱,可以看到 JavaScript 就是通过上面导出的 ccall 来调用 greet() 函数的:
  1. document.getElementById("mybutton").addEventListener("click", () => {
  2.        const result = Module.ccall(
  3.            "greet",         // name of C function
  4.            null,            // return type
  5.            ["string"],      // argument types
  6.            ["WebAssembly"]  // arguments
  7.        );
  8.        const n = parseInt(document.getElementById("myinput").value, 10);
  9.        const result2 = Module.ccall(
  10.            "fibonacci", // name of C function
  11.            "number",    // return type
  12.            ["number"],  // argument types
  13.            [n]          // arguments
  14.        );
  15.        console.log("The Fibonacci number is: " + result2);
  16.    });
复制代码
  除了 ccall,我们还可以使用 -s EXPORTED_RUNTIME_METHODS=ccall,cwrap 同时导出 ccall 和 cwrap 函数。ccall 的作用是直接调用某个 C 函数,而 cwrap 是将 C 函数编译为一个 JavaScript 函数,并可以反复调用,这在正式项目中更实用。
  点击这个按钮,可以在页面和控制台上都看到 greet() 函数打印的内容:

输入的数字太大,直接爆了...
  1. Uncaught RuntimeError: memory access out of bounds
复制代码
其他导入方式

在Nodejs中使用WebAssembly
好比我们有如下c代码 testWebAssembly.c
  1. #include <stdio.h>
  2. int output()
  3. {
  4.    return 1;
  5. }
  6. int add(int a, int b)
  7. {
  8.    return a + b;
  9. }
  10. emcc testWebAssembly.c -Os -s WASM=1 -s SIDE_MODULE=1 -o testWebAssembly.wasm
复制代码
上面的编译选项-s WASM=1是告诉编译器将代码编译成一个 WebAssembly 模块(即侧向模块),而不是一个完整的可执行程序。这意味着该程序不包含 main 函数,也不包含任何与操作体系或文件体系干系的代码。这使得该程序可以被其他程序通过 WebAssembly API 调用,而且可以在各种不同的环境中运行,比方欣赏器、Node.js 等。
我一开始编译确实-s SIDE_MODULE=1,导致编译出来的testWebAssembly.wasm在现实使用的时间不停报错如下,加了该选项之后就正常
  1. WebAssembly.Instance(): Import #0 module="wasi_snapshot_preview1" error: module is not an object or function
复制代码
-s WASM=1是 emsdk / emscripten 中的一个编译选项,它告诉 emscripten 编译器将输出编译为 WebAssembly 格式。详细来说,它启用了 Emscripten 的 WebAssembly 后端,该后端将 C/C++代码编译为 WebAssembly 模块。
在使用 Emscripten 编译 C/C++代码时,如果希望将其编译为 WebAssembly 格式,必须使用 -s WASM=1选项。如果您没有使用该选项,编译器将默以为 asm.js 格式进行编译。
WebAssembly 是一种新型的低级字节码格式,可以在欣赏器中运行,可以或许以可移植的方式在不同的平台上执行代码。相比于 asm.js,WebAssembly 具有更快的解析速率和更小的体积,这使得它成为用于在欣赏器中运行原生代码的最佳选择之一。
通过以上方式我们最终得到一个testWebAssembly.wasm
在Nodejs中使用WebAssembly
  1. const fs = require('fs');
  2.        const wasmCode = fs.readFileSync('./testWebAssembly.wasm');
  3.        // 编译WebAssembly二进制代码
  4.        const wasmModule = new WebAssembly.Module(toUint8Array(wasmCode));
  5.        // 实例化WebAssembly模块
  6.        const wasmInstance = new WebAssembly.Instance(wasmModule, {});
  7.        const lib = wasmInstance.exports;
  8.        // `Wasm` does **not** understand node buffers, but thankfully a node buffer
  9.        // is easy to convert to a native Uint8Array.
  10.        function toUint8Array(buf) {
  11.          const u = new Uint8Array(buf.length);
  12.          for (let i = 0; i < buf.length; ++i) {
  13.            u[i] = buf[i];
  14.          }
  15.          return u;
  16.        }
  17.        console.log(lib.add(5, 5));
  18.        console.log(lib.output());
复制代码
最终我们就能乐成在nodejs环境里直接调用c/c++代码的方法,使用方式比较简单,直接参看注释。



Python 编译成 WebAssembly

python转化成wasm,大了太多了
  1. [/code] [code]py2wasm pystone.py -o pystone.wasm
复制代码

pystone.py
  1. #! /usr/bin/env python3
  2. """
  3. "PYSTONE" Benchmark Program
  4. Version:        Python/1.1 (corresponds to C/1.1 plus 2 Pystone fixes)
  5. Author:         Reinhold P. Weicker,  CACM Vol 27, No 10, 10/84 pg. 1013.
  6.                 Translated from ADA to C by Rick Richardson.
  7.                 Every method to preserve ADA-likeness has been used,
  8.                 at the expense of C-ness.
  9.                 Translated from C to Python by Guido van Rossum.
  10. Version History:
  11.                 Version 1.1 corrects two bugs in version 1.0:
  12.                 First, it leaked memory: in Proc1(), NextRecord ends
  13.                 up having a pointer to itself.  I have corrected this
  14.                 by zapping NextRecord.PtrComp at the end of Proc1().
  15.                 Second, Proc3() used the operator != to compare a
  16.                 record to None.  This is rather inefficient and not
  17.                 true to the intention of the original benchmark (where
  18.                 a pointer comparison to None is intended; the !=
  19.                 operator attempts to find a method __cmp__ to do value
  20.                 comparison of the record).  Version 1.1 runs 5-10
  21.                 percent faster than version 1.0, so benchmark figures
  22.                 of different versions can't be compared directly.
  23. """
  24. LOOPS = 50000
  25. from time import clock
  26. __version__ = "1.1"
  27. [Ident1, Ident2, Ident3, Ident4, Ident5] = range(1, 6)
  28. class Record:
  29.     def __init__(self, PtrComp = None, Discr = 0, EnumComp = 0,
  30.                        IntComp = 0, StringComp = 0):
  31.         self.PtrComp = PtrComp
  32.         self.Discr = Discr
  33.         self.EnumComp = EnumComp
  34.         self.IntComp = IntComp
  35.         self.StringComp = StringComp
  36.     def copy(self):
  37.         return Record(self.PtrComp, self.Discr, self.EnumComp,
  38.                       self.IntComp, self.StringComp)
  39. TRUE = 1
  40. FALSE = 0
  41. def main(loops=LOOPS):
  42.     benchtime, stones = pystones(loops)
  43.     print("Pystone(%s) time for %d passes = %g" % \
  44.           (__version__, loops, benchtime))
  45.     print("This machine benchmarks at %g pystones/second" % stones)
  46. def pystones(loops=LOOPS):
  47.     return Proc0(loops)
  48. IntGlob = 0
  49. BoolGlob = FALSE
  50. Char1Glob = '\0'
  51. Char2Glob = '\0'
  52. Array1Glob = [0]*51
  53. Array2Glob = [x[:] for x in [Array1Glob]*51]
  54. PtrGlb = None
  55. PtrGlbNext = None
  56. def Proc0(loops=LOOPS):
  57.     global IntGlob
  58.     global BoolGlob
  59.     global Char1Glob
  60.     global Char2Glob
  61.     global Array1Glob
  62.     global Array2Glob
  63.     global PtrGlb
  64.     global PtrGlbNext
  65.     starttime = clock()
  66.     for i in range(loops):
  67.         pass
  68.     nulltime = clock() - starttime
  69.     PtrGlbNext = Record()
  70.     PtrGlb = Record()
  71.     PtrGlb.PtrComp = PtrGlbNext
  72.     PtrGlb.Discr = Ident1
  73.     PtrGlb.EnumComp = Ident3
  74.     PtrGlb.IntComp = 40
  75.     PtrGlb.StringComp = "DHRYSTONE PROGRAM, SOME STRING"
  76.     String1Loc = "DHRYSTONE PROGRAM, 1'ST STRING"
  77.     Array2Glob[8][7] = 10
  78.     starttime = clock()
  79.     for i in range(loops):
  80.         Proc5()
  81.         Proc4()
  82.         IntLoc1 = 2
  83.         IntLoc2 = 3
  84.         String2Loc = "DHRYSTONE PROGRAM, 2'ND STRING"
  85.         EnumLoc = Ident2
  86.         BoolGlob = not Func2(String1Loc, String2Loc)
  87.         while IntLoc1 < IntLoc2:
  88.             IntLoc3 = 5 * IntLoc1 - IntLoc2
  89.             IntLoc3 = Proc7(IntLoc1, IntLoc2)
  90.             IntLoc1 = IntLoc1 + 1
  91.         Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3)
  92.         PtrGlb = Proc1(PtrGlb)
  93.         CharIndex = 'A'
  94.         while CharIndex <= Char2Glob:
  95.             if EnumLoc == Func1(CharIndex, 'C'):
  96.                 EnumLoc = Proc6(Ident1)
  97.             CharIndex = chr(ord(CharIndex)+1)
  98.         IntLoc3 = IntLoc2 * IntLoc1
  99.         IntLoc2 = IntLoc3 / IntLoc1
  100.         IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1
  101.         IntLoc1 = Proc2(IntLoc1)
  102.     benchtime = clock() - starttime - nulltime
  103.     if benchtime == 0.0:
  104.         loopsPerBenchtime = 0.0
  105.     else:
  106.         loopsPerBenchtime = (loops / benchtime)
  107.     return benchtime, loopsPerBenchtime
  108. def Proc1(PtrParIn):
  109.     PtrParIn.PtrComp = NextRecord = PtrGlb.copy()
  110.     PtrParIn.IntComp = 5
  111.     NextRecord.IntComp = PtrParIn.IntComp
  112.     NextRecord.PtrComp = PtrParIn.PtrComp
  113.     NextRecord.PtrComp = Proc3(NextRecord.PtrComp)
  114.     if NextRecord.Discr == Ident1:
  115.         NextRecord.IntComp = 6
  116.         NextRecord.EnumComp = Proc6(PtrParIn.EnumComp)
  117.         NextRecord.PtrComp = PtrGlb.PtrComp
  118.         NextRecord.IntComp = Proc7(NextRecord.IntComp, 10)
  119.     else:
  120.         PtrParIn = NextRecord.copy()
  121.     NextRecord.PtrComp = None
  122.     return PtrParIn
  123. def Proc2(IntParIO):
  124.     IntLoc = IntParIO + 10
  125.     while 1:
  126.         if Char1Glob == 'A':
  127.             IntLoc = IntLoc - 1
  128.             IntParIO = IntLoc - IntGlob
  129.             EnumLoc = Ident1
  130.         if EnumLoc == Ident1:
  131.             break
  132.     return IntParIO
  133. def Proc3(PtrParOut):
  134.     global IntGlob
  135.     if PtrGlb is not None:
  136.         PtrParOut = PtrGlb.PtrComp
  137.     else:
  138.         IntGlob = 100
  139.     PtrGlb.IntComp = Proc7(10, IntGlob)
  140.     return PtrParOut
  141. def Proc4():
  142.     global Char2Glob
  143.     BoolLoc = Char1Glob == 'A'
  144.     BoolLoc = BoolLoc or BoolGlob
  145.     Char2Glob = 'B'
  146. def Proc5():
  147.     global Char1Glob
  148.     global BoolGlob
  149.     Char1Glob = 'A'
  150.     BoolGlob = FALSE
  151. def Proc6(EnumParIn):
  152.     EnumParOut = EnumParIn
  153.     if not Func3(EnumParIn):
  154.         EnumParOut = Ident4
  155.     if EnumParIn == Ident1:
  156.         EnumParOut = Ident1
  157.     elif EnumParIn == Ident2:
  158.         if IntGlob > 100:
  159.             EnumParOut = Ident1
  160.         else:
  161.             EnumParOut = Ident4
  162.     elif EnumParIn == Ident3:
  163.         EnumParOut = Ident2
  164.     elif EnumParIn == Ident4:
  165.         pass
  166.     elif EnumParIn == Ident5:
  167.         EnumParOut = Ident3
  168.     return EnumParOut
  169. def Proc7(IntParI1, IntParI2):
  170.     IntLoc = IntParI1 + 2
  171.     IntParOut = IntParI2 + IntLoc
  172.     return IntParOut
  173. def Proc8(Array1Par, Array2Par, IntParI1, IntParI2):
  174.     global IntGlob
  175.     IntLoc = IntParI1 + 5
  176.     Array1Par[IntLoc] = IntParI2
  177.     Array1Par[IntLoc+1] = Array1Par[IntLoc]
  178.     Array1Par[IntLoc+30] = IntLoc
  179.     for IntIndex in range(IntLoc, IntLoc+2):
  180.         Array2Par[IntLoc][IntIndex] = IntLoc
  181.     Array2Par[IntLoc][IntLoc-1] = Array2Par[IntLoc][IntLoc-1] + 1
  182.     Array2Par[IntLoc+20][IntLoc] = Array1Par[IntLoc]
  183.     IntGlob = 5
  184. def Func1(CharPar1, CharPar2):
  185.     CharLoc1 = CharPar1
  186.     CharLoc2 = CharLoc1
  187.     if CharLoc2 != CharPar2:
  188.         return Ident1
  189.     else:
  190.         return Ident2
  191. def Func2(StrParI1, StrParI2):
  192.     IntLoc = 1
  193.     while IntLoc <= 1:
  194.         if Func1(StrParI1[IntLoc], StrParI2[IntLoc+1]) == Ident1:
  195.             CharLoc = 'A'
  196.             IntLoc = IntLoc + 1
  197.     if CharLoc >= 'W' and CharLoc <= 'Z':
  198.         IntLoc = 7
  199.     if CharLoc == 'X':
  200.         return TRUE
  201.     else:
  202.         if StrParI1 > StrParI2:
  203.             IntLoc = IntLoc + 7
  204.             return TRUE
  205.         else:
  206.             return FALSE
  207. def Func3(EnumParIn):
  208.     EnumLoc = EnumParIn
  209.     if EnumLoc == Ident3: return TRUE
  210.     return FALSE
  211. if __name__ == '__main__':
  212.     import sys
  213.     def error(msg):
  214.         print(msg, end=' ', file=sys.stderr)
  215.         print("usage: %s [number_of_loops]" % sys.argv[0], file=sys.stderr)
  216.         sys.exit(100)
  217.     nargs = len(sys.argv) - 1
  218.     if nargs > 1:
  219.         error("%d arguments are too many;" % nargs)
  220.     elif nargs == 1:
  221.         try: loops = int(sys.argv[1])
  222.         except ValueError:
  223.             error("Invalid argument %r;" % sys.argv[1])
  224.     else:
  225.         loops = LOOPS
  226.     main(loops)
复制代码
将 go 编译成 WebAssembly

步骤一:准备 go 模块
准备一个简单的用于编译成 wasm 的 go 模块,主要的功能包罗:

  • 获取和输出文件的属性 getFileAttr();
  • JS 可调用的函数 jsPI();
  1. //go:build js && wasm
  2. package main
  3. import (
  4.         "fmt"
  5.         "math/rand"
  6.         "os"
  7.         "runtime"
  8.         "syscall"
  9.         "syscall/js"
  10.         "time"
  11. )
  12. func getFileAttr(path string) (string, error) {
  13.         fi, err := os.Stat(path)
  14.         if err != nil {
  15.                 return "", err
  16.         }
  17.         return fmt.Sprintf("Mode: %s\nOwner: %d\nGroup: %d\nSize: %d\nModified: %s\nIsDir: %t",
  18.                 fi.Mode(),
  19.                 fi.Sys().(*syscall.Stat_t).Uid,
  20.                 fi.Sys().(*syscall.Stat_t).Gid,
  21.                 fi.Size(),
  22.                 fi.ModTime().String(),
  23.                 fi.IsDir(),
  24.         ), nil
  25. }
  26. func main() {
  27.         // **ERROR**: fsnotify is not supported in WASM
  28.         fmt.Println("testing...........")
  29.         _, err := os.Lstat("/tmp/test.txt")
  30.         if err != nil {
  31.                 // as expected, lstat is not supported in WASM
  32.                 fmt.Println(err.Error())
  33.         }
  34.         ret, err := getFileAttr("/tmp/test.txt")
  35.         if err != nil {
  36.                 fmt.Println(err.Error())
  37.         }
  38.         fmt.Println(ret)
  39.         js.Global().Set("jsPI", jsPI())
  40.         js.Global().Call("alert", "this is an alerting!")
  41.         v := js.Global().Get("app")
  42.         fmt.Println(v.Get("title").String())
  43.         fmt.Println(v.Call("getAppTitle", "").String())
  44.         select {}
  45. }
  46. func pi(samples int) float64 {
  47.         cpus := runtime.NumCPU()
  48.         threadSamples := samples / cpus
  49.         results := make(chan float64, cpus)
  50.         for j := 0; j < cpus; j++ {
  51.                 go func() {
  52.                         var inside int
  53.                         r := rand.New(rand.NewSource(time.Now().UnixNano()))
  54.                         for i := 0; i < threadSamples; i++ {
  55.                                 x, y := r.Float64(), r.Float64()
  56.                                 if x*x+y*y <= 1 {
  57.                                         inside++
  58.                                 }
  59.                         }
  60.                         results <- float64(inside) / float64(threadSamples) * 4
  61.                 }()
  62.         }
  63.         var total float64
  64.         for i := 0; i < cpus; i++ {
  65.                 total += <-results
  66.         }
  67.         return total / float64(cpus)
  68. }
  69. func jsPI() js.Func {
  70.         return js.FuncOf(func(this js.Value, args []js.Value) any {
  71.                 if len(args) != 1 {
  72.                         return "Invalid no of arguments passed"
  73.                 }
  74.                 samples := args[0].Int()
  75.                 return pi(samples)
  76.         })
  77. }
复制代码
步骤二:go wasm 编译
为了可以或许在欣赏器或者 Obsidian 环境中运行 go wasm 文件,起首需要 go 提供的 JS 文件 wasm_exec.js,然后指定编译环境变量 GOOS=js GOARCH=wasm 对 go 模块进行编译,详细下令如下所示:
  1. cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" ./test/
  2. cp "$(go env GOROOT)/misc/wasm/wasm_exec_node.js" ./test/
  3. GOOS=js GOARCH=wasm go build -o ./bin/md-monitor.wasm
复制代码
步骤三:JS 启动 wasm
可以现在 Obsidian console 环境中测试运行 go wasm:

  • 导入 go wasm_exec 包,const exec_wasm = require('./test/wasm_exec.js');
  • 初始化 go wasm 实例,const go = new Go();
  • 读取 wasm 文件并初始化实例并运行,WebAssembly.instantiate()
  • 调用 wasm 定义的函数进行测试,jsPI(3)
  1. 'use strict'
  2. // Try to execute the following code in browser or electron(Obsidian) console
  3. const exec_wasm = require('./test/wasm_exec.js');
  4. const go = new Go();
  5. const fs = require('fs');
  6. let content = undefined;
  7. fs.readFile("./bin/md-monitor.wasm", (err, data) => { content = data });
  8. WebAssembly.instantiate(content, go.importObject).then((ret) => { go.run(ret.instance); });
  9. // call exported function which define in go module.
  10. jsPI(3);
复制代码



参考资料
Emscripten 的官方文档 Download and install — Emscripten 3.1.60-git (dev) documentation
WebAssembly 概念
WebAssembly 概念 - WebAssembly | MDN (mozilla.org)


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

农妇山泉一亩田

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表