我首先阅读了Why is my WebAssembly function slower than the JavaScript equivalent?
的答案但它对这个问题没什么了解,而且我投入了大量的时间,很可能是那些黄色的东西。
我不使用全局变量,我不使用任何内存。我有两个简单的函数,可以找到一个线段的长度,并将它们与普通的旧Javascript中的相同内容进行比较。我有4个参数3个本地人并返回一个浮点数或双倍。
在Chrome上,Javascript比webAssembly快40倍,而在Firefox上,ism几乎比Javascript慢<300> 。
我已将测试用例添加到jsPref WebAssembly V Javascript math
无论
请选择1。
通过定位嵌入较大版本的WebAssembly来重用现有代码 JavaScript / HTML应用程序。这可能是简单的任何事情 辅助库,面向计算任务卸载。
我希望我可以用webAssembly替换一些几何库来获得额外的性能。我希望它会很棒,比10倍或更快。但是WTF慢了300倍。
这不是JS优化问题。
为确保优化尽可能少,我已使用以下方法测试,以减少或消除任何优化偏差。
c += length(...
以确保执行所有代码。 bigCount += c
以确保执行整个功能。不需要Math.hypot
在JS中添加较慢的长度计算,以证明代码正在运行。
// setup and associated functions
const setOf = (count, callback) => {var a = [],i = 0; while (i < count) { a.push(callback(i ++)) } return a };
const rand = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;
const a = setOf(100009,i=>rand(-100000,100000));
var bigCount = 0;
function len(x,y,x1,y1){
var nx = x1 - x;
var ny = y1 - y;
return Math.sqrt(nx * nx + ny * ny);
}
function lenSlow(x,y,x1,y1){
var nx = x1 - x;
var ny = y1 - y;
return Math.hypot(nx,ny);
}
function lenEmpty(x,y,x1,y1){
return x;
}
// Test functions in same scope as above. None is in global scope
// Each function is copied 4 time and tests are performed randomly.
// c += length(... to ensure all code is executed.
// bigCount += c to ensure whole function is executed.
// 4 lines for each function to reduce a inlining skew
// all values are randomly generated doubles
// each function call returns a different result.
tests : [{
func : function (){
var i,c=0,a1,a2,a3,a4;
for (i = 0; i < 10000; i += 1) {
a1 = a[i];
a2 = a[i+1];
a3 = a[i+2];
a4 = a[i+3];
c += length(a1,a2,a3,a4);
c += length(a2,a3,a4,a1);
c += length(a3,a4,a1,a2);
c += length(a4,a1,a2,a3);
}
bigCount = (bigCount + c) % 1000;
},
name : "length64",
},{
func : function (){
var i,c=0,a1,a2,a3,a4;
for (i = 0; i < 10000; i += 1) {
a1 = a[i];
a2 = a[i+1];
a3 = a[i+2];
a4 = a[i+3];
c += lengthF(a1,a2,a3,a4);
c += lengthF(a2,a3,a4,a1);
c += lengthF(a3,a4,a1,a2);
c += lengthF(a4,a1,a2,a3);
}
bigCount = (bigCount + c) % 1000;
},
name : "length32",
},{
func : function (){
var i,c=0,a1,a2,a3,a4;
for (i = 0; i < 10000; i += 1) {
a1 = a[i];
a2 = a[i+1];
a3 = a[i+2];
a4 = a[i+3];
c += len(a1,a2,a3,a4);
c += len(a2,a3,a4,a1);
c += len(a3,a4,a1,a2);
c += len(a4,a1,a2,a3);
}
bigCount = (bigCount + c) % 1000;
},
name : "length JS",
},{
func : function (){
var i,c=0,a1,a2,a3,a4;
for (i = 0; i < 10000; i += 1) {
a1 = a[i];
a2 = a[i+1];
a3 = a[i+2];
a4 = a[i+3];
c += lenSlow(a1,a2,a3,a4);
c += lenSlow(a2,a3,a4,a1);
c += lenSlow(a3,a4,a1,a2);
c += lenSlow(a4,a1,a2,a3);
}
bigCount = (bigCount + c) % 1000;
},
name : "Length JS Slow",
},{
func : function (){
var i,c=0,a1,a2,a3,a4;
for (i = 0; i < 10000; i += 1) {
a1 = a[i];
a2 = a[i+1];
a3 = a[i+2];
a4 = a[i+3];
c += lenEmpty(a1,a2,a3,a4);
c += lenEmpty(a2,a3,a4,a1);
c += lenEmpty(a3,a4,a1,a2);
c += lenEmpty(a4,a1,a2,a3);
}
bigCount = (bigCount + c) % 1000;
},
name : "Empty",
}
],
因为测试中有更多的开销,结果更接近但JS代码仍然快了两个数量级。
注意函数Math.hypo
t的速度有多慢。如果优化有效,那么函数将接近更快的len
函数。
/*
=======================================
Performance test. : WebAssm V Javascript
Use strict....... : true
Data view........ : false
Duplicates....... : 4
Cycles........... : 147
Samples per cycle : 100
Tests per Sample. : undefined
---------------------------------------------
Test : 'length64'
Mean : 12736µs ±69µs (*) 3013 samples
---------------------------------------------
Test : 'length32'
Mean : 13389µs ±94µs (*) 2914 samples
---------------------------------------------
Test : 'length JS'
Mean : 728µs ±6µs (*) 2906 samples
---------------------------------------------
Test : 'Length JS Slow'
Mean : 23374µs ±191µs (*) 2939 samples << This function use Math.hypot
rather than Math.sqrt
---------------------------------------------
Test : 'Empty'
Mean : 79µs ±2µs (*) 2928 samples
-All ----------------------------------------
Mean : 10.097ms Totals time : 148431.200ms 14700 samples
(*) Error rate approximation does not represent the variance.
*/
如果没有优化,那么WebAssmbly的重点是什么
更新结束
查找一条线的长度。
自定义语言的原始资料
// declare func the < indicates export name, the param with types and return type
func <lengthF(float x, float y, float x1, float y1) float {
float nx, ny, dist; // declare locals float is f32
nx = x1 - x;
ny = y1 - y;
dist = sqrt(ny * ny + nx * nx);
return dist;
}
// and as double
func <length(double x, double y, double x1, double y1) double {
double nx, ny, dist;
nx = x1 - x;
ny = y1 - y;
dist = sqrt(ny * ny + nx * nx);
return dist;
}
代码编译为Wat以进行校对
(module
(func
(export "lengthF")
(param f32 f32 f32 f32)
(result f32)
(local f32 f32 f32)
get_local 2
get_local 0
f32.sub
set_local 4
get_local 3
get_local 1
f32.sub
tee_local 5
get_local 5
f32.mul
get_local 4
get_local 4
f32.mul
f32.add
f32.sqrt
)
(func
(export "length")
(param f64 f64 f64 f64)
(result f64)
(local f64 f64 f64)
get_local 2
get_local 0
f64.sub
set_local 4
get_local 3
get_local 1
f64.sub
tee_local 5
get_local 5
f64.mul
get_local 4
get_local 4
f64.mul
f64.add
f64.sqrt
)
)
以十六进制字符串编译的wasm(注意不包括名称部分)并使用WebAssembly.compile加载。导出的函数然后针对Javascript函数len(在下面的代码片段中)运行
// hex of above without the name section
const asm = `0061736d0100000001110260047d7d7d7d017d60047c7c7c7c017c0303020001071402076c656e677468460000066c656e67746800010a3b021c01037d2002200093210420032001932205200594200420049492910b1c01037c20022000a1210420032001a122052005a220042004a2a09f0b`
const bin = new Uint8Array(asm.length >> 1);
for(var i = 0; i < asm.length; i+= 2){ bin[i>>1] = parseInt(asm.substr(i,2),16) }
var length,lengthF;
WebAssembly.compile(bin).then(module => {
const wasmInstance = new WebAssembly.Instance(module, {});
lengthF = wasmInstance.exports.lengthF;
length = wasmInstance.exports.length;
});
// test values are const (same result if from array or literals)
const a1 = rand(-100000,100000);
const a2 = rand(-100000,100000);
const a3 = rand(-100000,100000);
const a4 = rand(-100000,100000);
// javascript version of function
function len(x,y,x1,y1){
var nx = x1 - x;
var ny = y1 - y;
return Math.sqrt(nx * nx + ny * ny);
}
所有3个函数的测试代码相同,并以严格模式运行。
tests : [{
func : function (){
var i;
for (i = 0; i < 100000; i += 1) {
length(a1,a2,a3,a4);
}
},
name : "length64",
},{
func : function (){
var i;
for (i = 0; i < 100000; i += 1) {
lengthF(a1,a2,a3,a4);
}
},
name : "length32",
},{
func : function (){
var i;
for (i = 0; i < 100000; i += 1) {
len(a1,a2,a3,a4);
}
},
name : "lengthNative",
}
]
FireFox上的测试结果是
/*
=======================================
Performance test. : WebAssm V Javascript
Use strict....... : true
Data view........ : false
Duplicates....... : 4
Cycles........... : 34
Samples per cycle : 100
Tests per Sample. : undefined
---------------------------------------------
Test : 'length64'
Mean : 26359µs ±128µs (*) 1128 samples
---------------------------------------------
Test : 'length32'
Mean : 27456µs ±109µs (*) 1144 samples
---------------------------------------------
Test : 'lengthNative'
Mean : 106µs ±2µs (*) 1128 samples
-All ----------------------------------------
Mean : 18.018ms Totals time : 61262.240ms 3400 samples
(*) Error rate approximation does not represent the variance.
*/
答案 0 :(得分:6)
Andreas描述了JavaScript实现为initially observed to be x300 faster的原因。但是,您的代码还存在许多其他问题。
有关更明确的答案,请参阅WebAssembly小组的联合文件,其中概述了预期的runtime performance gain of around 30%
最后,回答你的观点:
如果没有优化,那么WebAssembly的重点是什么
我认为您对WebAssembly将为您做什么有误解。基于上面的论文,运行时性能优化是非常适度的。但是,仍有许多性能优势:
也有许多与绩效无关的优势。
要获得更真实的性能测量,请查看:
两者都是实用的生产代码库。
答案 1 :(得分:4)
JS引擎可以对此示例应用大量动态优化:
使用整数执行所有计算,并且仅在最终调用Math.sqrt时转换为double。
内联对len
功能的调用。
将计算提升出循环,因为它总是计算相同的东西。
认识到循环是空的并完全消除它。
认识到结果永远不会从测试函数返回,因此会删除整个测试函数。
即使添加每个通话的结果,除了(4)之外的所有通知都适用。使用(5)最终结果是一个空函数。
使用Wasm,引擎无法完成大部分步骤,因为它无法跨越语言边界(至少今天没有引擎可以做到这一点,AFAICT)。此外,对于Wasm,假设生产(离线)编译器已经执行了相关的优化,因此对于JavaScript来说,Wasm JIT的攻击性往往低于JavaScript,而静态优化是不可能的。