本文作者:IMWeb llunnn 原文出处:IMWeb社区 未经同意,禁止转载
关于WebAssembly (en zh) 就不多说了,这是一个可移植、体积小、加载快并且兼容 Web 的全新格式。这里本人尝试了开发环境的搭建,并接入了一个C++编写的计算字符串MD5的自定义方法。
基本的环境搭建可以参考mdn文档和emscripten-site,将C/C++编译为wasm依赖于emscripten,这里我们需要自行去编译一个Emscripten。
这里用的是win10的环境,在windows上搭环境对(入门级)程序员来说真的是一大噩梦了叭(心累)。加之emscripten也有过不少的更新,网上的博客、StackOverflow等的解答很多已经不适用了,在摸爬滚打下...终于...我放弃了windows(。)
这里记录一下踩过的一些坑吧,虽然最后没有趟出来...
还好,在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安装一下,再按照官方文档做简单配置就可以了。(强推这种方式,整个安装编译过程基本没有遇到问题)
在ubuntu子系统中,可以在/mnt目录下访问windows各盘的文件。
hint: emscripten FAQ上有许多十分有用的常见问题及其解答。推荐先在上面寻找答案,再去动用搜索引擎(毕竟emscripten版本可能有变化)。
可以先按照mdn上的样例代码尝试生成一个hello world的例子。
可能遇到的一些问题
如果输出hello world成功了,环境的搭建应该没什么问题。
这里尝试接入一个用C++编写的对字符串计算MD5摘要的函数。网上有许多cpp的实现,可以随意找一种进行尝试。
这里打算用emscripten生成ccall函数的能力来调用C++函数。ccall可支持js和wasm之间传递number, string和array,其中string对应了js的String和C++的char*。
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
我们在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后跟的是编译时的选项:
-o后跟的是编译目标,我们生成一个js文件,其中会附带帮助我们运行wasm的“胶水代码”,其中包括了ccall。
执行成功后,我们新建一个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++函数。