前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++ 编写 WebAssembly初探

C++ 编写 WebAssembly初探

作者头像
IMWeb前端团队
发布2019-12-04 16:59:29
2.1K0
发布2019-12-04 16:59:29
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队

本文作者:IMWeb llunnn 原文出处:IMWeb社区 未经同意,禁止转载

关于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的函数:

代码语言:javascript
复制
#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。

代码语言:javascript
复制
<!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来获取字符串。

代码语言:javascript
复制
<!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的计算速度还是非常可观的:

代码语言:javascript
复制
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++函数。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-01-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 环境搭建
  • 入门
  • 尝试执行用C++编写的函数
    • C++部分
      • 编译C++文件
        • 引入生成的js
        • 不太靠谱的比较
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档