C++ 编写 WebAssembly初探

栏目: C++ · 发布时间: 5年前

内容简介:关于WebAssembly (基本的环境搭建可以参考这里用的是win10的环境,在windows上搭环境对(入门级)程序员来说真的是一大噩梦了叭(心累)。加之emscripten也有过不少的更新,网上的博客、StackOverflow等的解答很多已经不适用了,在摸爬滚打下...终于...我放弃了windows(。)

关于WebAssembly ( en zh ) 就不多说了,这是一个可移植、体积小、加载快并且兼容 Web 的全新格式。这里本人尝试了开发环境的搭建,并接入了一个C++编写的计算字符串MD5的自定义方法。

环境搭建

基本的环境搭建可以参考 mdn文档emscripten-site ,将C/C++编译为wasm依赖于emscripten,这里我们需要自行去编译一个Emscripten。

这里用的是win10的环境,在windows上搭环境对(入门级)程序员来说真的是一大噩梦了叭(心累)。加之emscripten也有过不少的更新,网上的博客、StackOverflow等的解答很多已经不适用了,在摸爬滚打下...终于...我放弃了windows(。)

这里记录一下踩过的一些坑吧,虽然最后没有趟出来...

  • vs2017不能使用过高的版本(15.8+),会导致编译Emcripten失败。
  • 如果编译过程失败后,重装了visual studio,CMakeList上记录的vs版本会与现有版本不一致,(本人是emsdk uninstall了之前编译的模块)
  • 尽量不要将emsdk需要的环境变量加到全局,包括node, python等,指向其内置安装的版本。加到全局PATH会覆盖掉原本在使用的版本。

还好,在emscripten的文档看到了这句话:

Instead of running emscripten on Windows directly, you can use the Windows Subsystem for Linux to run it in a Linux environment.

win10下支持安装 Ubuntu子系统 ,去Microsoft Store安装一下,再按照官方文档做简单配置就可以了。(强推这种方式,整个安装编译过程基本没有遇到问题)

emscripten Linux安装参考

在ubuntu子系统中,可以在/mnt目录下访问windows各盘的文件。

hint: emscripten FAQ 上有许多十分有用的常见问题及其解答。推荐先在上面寻找答案,再去动用搜索引擎(毕竟emscripten版本可能有变化)。

入门

可以先按照mdn上的 样例代码 尝试生成一个hello world的例子。

可能遇到的一些问题

  • 需要用fetch加载wasm文件,为了方便,可以直接在生成的文件目录下起一个 http-server
  • printf的内容要以换行结尾,否则输出的内容不会输出到控制台中。
  • 生成wasm及胶水代码的过程中如果链接库出错了,可以先clear cache再重新尝试。

尝试执行用C++编写的函数

如果输出hello world成功了,环境的搭建应该没什么问题。

这里尝试接入一个用C++编写的对 字符串 计算MD5摘要的函数。网上有许多cpp的实现,可以随意找一种进行尝试。

这里打算用emscripten生成 ccall 函数的能力来调用C++函数。ccall可支持js和wasm之间传递number, string和array,其中string对应了js的String和C++的char*。

C++部分

C++中对外暴露一个接收char*,生成char* MD5的函数:

#ifdef __cplusplus
extern "C"
{
#endif
    // 总之这个函数接收一个char*作为参数,返回一个char*的md5,各人实现不同。
    char *getMD5(char *M)
    {
        string str = M;
        MD5 *pMD5 = new MD5(str);
        char *md5 = (char *)(pMD5->getDigest().c_str());
        delete pMD5;
        return md5;
    }

#ifdef __cplusplus
}
#endif

编译C++文件

我们在ubuntu子系统下进入到文件目录,编译这个C++文件,执行:

emcc md5.cpp -s "EXPORTED_FUNCTIONS=['_getMD5']" -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall']" -o md5.js

【这里使用的emcc版本是1.38.21】

其中md5.cpp为我的C++文件名称

-s后跟的是编译时的选项:

  • EXPORTED_FUNCTIONS值需要暴露给js调用的自定义函数,对应Cpp文件中的函数,在其函数名前前加一个下划线
  • EXTRA_EXPORTED_RUNTIME_METHODS后跟我们需要调用的运行时方法,这里是'ccall'。

-o后跟的是编译目标,我们生成一个js文件,其中会附带帮助我们运行wasm的“胶水代码”,其中包括了ccall。

引入生成的js

执行成功后,我们新建一个md5.html,引入md5.js。

<!DOCTYPE html>
<html>
  <head>
    <title>wasm_md5</title>
  </head>
  <body>
    <script type="text/javascript" src="md5.js"></script>
    <script>
      console.log(Module.ccall(
        "getMD5", // C函数的名称,这里不需要下划线
        "string", // 返回值的类型
        ["string"], // 参数列表的类型
        ['123'], // 参数列表
      ));
    </script>
  </body>
</html>

生成的md5.js在全局添加了一个Module对象,其中有ccall方法,我们可以调用这个方法来计算MD5。

(123的MD5是202cb962ac59075b964b07152d234b70,可以检验是否计算成功~)

不太靠谱的比较

尝试使用cdn提供的js计算md5,与wasm计算进行时间比较。

(因为这里双方的算法可能存在一定差别,结果 可能并不可靠

为了方便生成不同的字符串,这里通过上传文件并使用FileReader将其转为base64的DataURL来获取字符串。

<!DOCTYPE html>
<html>
  <head>
    <title>wasm_md5</title>
  </head>
  <body>
    <input type="file" onchange="handleFiles(this.files)" />
    <script type="text/javascript" src="md5.js"></script>
    <!-- 加载一个cdn上计算md5的库 -->
    <script src="http://cdn.bootcss.com/blueimp-md5/1.1.0/js/md5.min.js"></script>
    <script>
      function handleFiles(files) {
        const file = files[0];
        const urlReader = new FileReader();
        urlReader.readAsDataURL(file);
        urlReader.addEventListener(
          "load",
          () => {
            compareMD5(urlReader.result);
          },
          false
        );
      }
      function compareMD5(str) {
        console.log("string length:", str.length);
        let beforeMD5, afterMD5;
        beforeMD5 = Date.now();
        console.log(
          "wasm",
          Module.ccall("getMD5", "string", ["string"], [str])
        );
        afterMD5 = Date.now();
        const wasmTime = afterMD5 - beforeMD5;
        beforeMD5 = Date.now();
        console.log("js", md5(str));
        afterMD5 = Date.now();
        const jsTime = afterMD5 - beforeMD5;
        console.log("wasm:", wasmTime, "js:", jsTime);
      }
    </script>
  </body>
</html>

贴几组不同长度的字符串计算md5的结果和时间比较,可以看见计算出的MD5是 相同 的,而比较之下wasm的计算速度还是 非常可观 的:

md5.html:string length: 743045
md5.html:27 wasm faa60598ea6af673883b2a5d71c3b669
md5.html:34 js faa60598ea6af673883b2a5d71c3b669
md5.html:37 wasm: 9 js: 63

md5.html:24 string length: 1250065
md5.html:27 wasm 0d306793f52ae52392abe86f9f445a0e
md5.html:34 js 0d306793f52ae52392abe86f9f445a0e
md5.html:37 wasm: 17 js: 135

md5.html:24 string length: 8769
md5.html:27 wasm 769ec5c2a9a0934c864b435878773722
md5.html:34 js 769ec5c2a9a0934c864b435878773722
md5.html:37 wasm: 0 js: 5

md5.html:string length: 144349
md5.html:27 wasm b119c18e331516c3ca6158fd2c5a6d8e
md5.html:34 js b119c18e331516c3ca6158fd2c5a6d8e
md5.html:37 wasm: 9 js: 29

md5.html:24 string length: 311285
md5.html:27 wasm aad59131791b96dcd8a518bbb1723905
md5.html:34 js aad59131791b96dcd8a518bbb1723905
md5.html:37 wasm: 7 js: 62

至此,我们成功通过wasm接入了一个计算字符串MD5的C++函数。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTML5

HTML5

Matthew David / Focal Press / 2010-07-29 / USD 39.95

Implement the powerful new multimedia and interactive capabilities offered by HTML5, including style control tools, illustration tools, video, audio, and rich media solutions. Understand how HTML5 is ......一起来看看 《HTML5》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具