当在浏览器中运行一个完整的WebAssembly应用时,
需要经历:从远程位置下载模块二进制文件、下载用于连接上层JavaScript环境与模块实例的“胶水”脚本文件、初始化模块需要使用的内存和变量资源等环节。
而一旦其中某个环节消耗的时间过长,WebAssembly本身所带来的性能优势便无法得到完全体现,甚至应用的整体运行效率可能还会低于具有同样功能但完全基于JavaScript 语言编写的应用。
因此:我们可以从减小生成模块的文件体积、模块对象的加载与重用以及“胶水”脚本文件的代码压缩等多个方面来优化并提高Wasm应用的整体运行效率。
快排例子
index.cc
#include <iostream>
#include <vector>
#include <emscripten.h>
using namespace std;
void swap(char* a,char* b){
char t =*a;
*a=*b;
*b=t;
}
void printArr(char arr[],char length){
vector<char> t(arr,arr+length);
for(auto &e:t){
cout<<(int)e<<" ";
}
cout<<endl;
}
char partition (char arr[],char low,char high){
char pivot = arr[high];
char i =(low-1);
for(char j=low;j<=high-1;j++){
if(arr[j]<=pivot){
i++;
swap(&arr[i],&arr[j]);
}
}
swap(&arr[i+1],&arr[high]);
return (i+1);
}
extern "C" char* EMSCRIPTEN_KEEPALIVE quickSort(char arr[],char low,char high){
if(low<high){
printArr(arr,high+1);
char pi = partition(arr,low,high);
quickSort(arr,low,pi-1);
quickSort(arr,pi+1,high);
}
return arr;
}
附加post-script.js代码
__ATPOSTRUN__.push(() =>{
//创建一个WebIDL对象
var array = [11,24,36,42,4,25,9,1,0];
var arrayPointer = Module['cwrap']('quickSort','number',['array','number','number'])(array,0,array.length-1);
var clearArrResult = [];
for(let i =0;i<array.length;i++){
clearArrResult.push(Module['getValue'](arrayPointer+i,'i8'));
}
console.log(clearArrResult);
});
命令
emcc index.cc --std=c++11 -s WASM=1 -o index.js --post-js post-script.js -s EXTRA_EXPORTED_RUNTIME_METHODS="['cwrap','getValue']"
运行结果
加入性能测试代码
附件在胶水代码最前面的pre-script.js代码
var startTimestamp = performance.now();
修改附加post-script.js代码
__ATPOSTRUN__.push(() =>{
//创建一个WebIDL对象
var array = [11,24,36,42,4,25,9,1,0];
var arrayPointer = Module['cwrap']('quickSort','number',['array','number','number'])(array,0,array.length-1);
var clearArrResult = [];
for(let i =0;i<array.length;i++){
clearArrResult.push(Module['getValue'](arrayPointer+i,'i8'));
}
console.log(clearArrResult);
var time = performance.timing;
var loadingTime = time.loadEventStart - time.navigationStart;
var executionTime = performance.now() - startTimestamp;
var totalTime = new Date().getTime() - time.navigationStart;
//打印应用的脚本执行时间
console.log("Application ET:",Math.round(executionTime),"ms");
//打印应用的脚本下载时间和初始化时间
console.log("Application LT:",loadingTime,"ms");
//打印应用的整体时间
console.log("Application TT:",totalTime,"ms");
});
再次运行结果
1.使用编译器代码优化策略
一般是-O3这个选项
命令
emcc index.cc -O3 --std=c++11 -s WASM=1 -o index.js --post-js post-script.js --pre-js pre-script.js -s EXTRA_EXPORTED_RUNTIME_METHODS="['cwrap','getValue']"
文件大小
性能时间
O3优化
2.使用GCC压缩代码
这里我遇到ES6语法不支持,所以把它修改成了ES5 在线修改地址
https://babeljs.io/repl
__ATPOSTRUN__.push(function () {
//创建一个WebIDL对象
var array = [11,24,36,42,4,25,9,1,0];
var arrayPointer = Module['cwrap']('quickSort','number',['array','number','number'])(array,0,array.length-1);
var clearArrResult = [];
for(var i =0;i<array.length;i++){
clearArrResult.push(Module['getValue'](arrayPointer+i,'i8'));
}
console.log(clearArrResult);
var time = performance.timing;
var loadingTime = time.loadEventStart - time.navigationStart;
var executionTime = performance.now() - startTimestamp;
var totalTime = new Date().getTime() - time.navigationStart;
//打印应用的脚本执行时间
console.log("Application ET:",Math.round(executionTime),"ms");
//打印应用的脚本下载时间和初始化时间
console.log("Application LT:",loadingTime,"ms");
//打印应用的整体时间
console.log("Application TT:",totalTime,"ms");
});
由于GCC本身是基于Java语言编写的,因此在使用该优化器前,请确认本机已经预装了JRE (Java 运行时环境),并确保可以在命令行的全局环境下直接调用java 命令。
相比于我们常用的
UglifyJS压缩库,GCC在JavaScript代码的压缩优化策略上会显得更加激进,在某种程度上可能会具有一-定的破坏性,即在压缩过程中破坏了原始代码的可读性结构。但也正是由于这种对源代码的“分析破坏”和“再重构”过程,使得GCC成为压缩率最高的JavaScript代码优化工具。如果想要使用GCC对当前的Wasm应用进行优化,则需要对上层的JavaScript 脚本代码做出一些改变。在调用Emscripten运行时环境所提供的cwarp方法时,是通过
Module[cwrap]这种字面量属性值的方式来调用的。而这就是专门为使用GCC而做出的改变。实际上,在使用GCC压缩代码时,它会将所有Module.cwrap这种属性值形式的函数调用过程进行重构,并压缩属性名的字符串长度。而以字面量属性值形式表示的调用过程则会被原封不动地保留下来。因此,这里需要与Emscripten “胶水”脚本内部的函数
通过添加参数--closure 1
命令
emcc index.cc -O3 --closure 1 --std=c++11 -s WASM=1 -o index.js --post-js post-script.js --pre-js pre-script.js -s EXTRA_EXPORTED_RUNTIME_METHODS="['cwrap','getValue']"
只有28.7KB









网友评论