什么是WebAssembly?
集会?
众所周知,机器读取二进制代码(零和元素),因为它对它们非常方便,但对人类来说不是。因此,工程师基本上创建了二进制代码的映射,并将映射版本称为“ assembly”。
WebAssembly?
WebAssembly是一种类似“汇编”的语言。它的昵称是 wasm 。基本上WebAssembly是现代浏览器的汇编语言。
人们通常不会直接编写汇编代码,因为它可能很乏味。这就是为什么有许多装配编译器只需从C/C++,C#,Rust,Java创建汇编代码...因此,基本上您可以在浏览器内运行现有的C/C ++或Rust应用程序。
WebAssembly代码可以直接注入JavaScript或Node.js环境中。这可能非常有用,因为您可以将WebAssembly与JavaScript结合在一起。
WebAssembly几乎提供本地性能。 JavaScript已编译(JIT)。但是WebAssembly是预编译的。因此,这就是为什么WASM可以比JavaScript代码更快地执行WASM。
弄脏你的手
首先,本文中使用的所有源代码在GitHub中可用。我脑海中最终的问题是“我们可以用websembly更快地对整数排序?” 。由于我们想更快地执行,因此我认为我应该使用圣编程语言 c 。
为了编译WebAssembly,我使用了emscripten我编写了C代码并将C代码编译为使用Emscripten的WebAssembly。我只是在MDN和emscripten
你好世界!
通过遵循其文档下载并安装Emscripten后,您应该有一个emsdk
文件夹。进入该文件夹并运行source ./emsdk_env.sh
。现在,您可以将C代码编译为WebAssembly。为了编译Hello World WebAssembly代码,我使用了Command emcc -o hello3.html hello3.c --shell-file html_template/shell_minimal2.html -s NO_EXIT_RUNTIME=1 -s "EXPORTED_RUNTIME_METHODS=['ccall']"
。在这里,emcc
是Emscripten编译器。 -o hello3.html hello3.c
表示编译“ Hello3.3c”,输出HTML文件'Hello3.html'。我的“ hello3.3c”就像下面
#include <stdio.h>
#include <emscripten/emscripten.h>
int main() {
printf("Hello World\n");
return 0;
}
#ifdef __cplusplus
#define EXTERN extern "C"
#else
#define EXTERN
#endif
EXTERN EMSCRIPTEN_KEEPALIVE void myFunction(int argc, char ** argv) {
printf("MyFunction Called\n");
}
加载网页时,它只是将“ Hello World”打印到开发人员控制台。并在调用C函数时也打印“称为“称为”的myfunction。
该脚本的 --shell-file html_template/shell_minimal2.html
基本上使用模板html文件来生成输出“ hello3.html”文件。我的“ shell_minimal2.html”如下。
<html>
<body>
<button id="mybutton">Call the C function</button>
</body>
</html>
{{{ SCRIPT }}}
<script>
document.getElementById("mybutton").addEventListener("click", () => {
alert("check console");
const result = Module.ccall(
"myFunction", // name of C function
null, // return type
null, // argument types
null // arguments
);
});
</script>
使用某种称为ccal
的特殊方法调用该函数的其余部分是必要的。如果执行命令,它将生成“ Hello3.html”,“ Hello3.js”和“ Hello3.wasm”文件。如果您查看“ Hello3.html”,您将看到
<html>
<body>
<button id="mybutton">Call the C function</button>
</body>
</html>
<script async type="text/javascript" src="hello3.js"></script>
<script>
document.getElementById("mybutton").addEventListener("click", () => {
alert("check console");
const result = Module.ccall(
"myFunction", // name of C function
null, // return type
null, // argument types
null // arguments
);
});
</script>
模板文件'shell_minimal2.html'和'hello3.html'之间的唯一区别是{{{ SCRIPT }}}
部分。在“ hello3.html”中,该部分被<script async type="text/javascript" src="hello3.js">
'hello3.js'替换为一个JavaScript文件,它提供了一个名为Module
的全局变量,还导入了hello3.wasm
。 hello3.wasm
是一个二进制文件。您不能轻松地用肉眼阅读它。启动HTTP服务器并在现代浏览器中打开“ Hello3.html”。我正在使用VSCODE进行代码编辑,并且它具有简单HTTP服务器的a nice extension。然后,您实际上可以看到WASM文件已转换为浏览器中的WebAssembly文本(.wat)格式。
在开发人员控制台中,您可以看到printf
C码的语句实际上是打印的。这是WebAssembly的Hello World!另外,如果您单击该按钮,您将看到它调用C函数!太棒了!从JavaScript我们可以调用C函数!
斐波那契
现在,让我们看看C代码是否可以比普通JavaScript代码更快执行。为了进行比较,我在C和JavaScript中以非常低效的方式实现了斐波那契算法,并并排执行。
我执行命令,就像Hello World示例emcc -o hello4.html fib.c --shell-file html_template/simple_compare.html -sEXPORTED_FUNCTIONS=_fib -sEXPORTED_RUNTIME_METHODS=cwrap
一样。在这里调用C函数,我们使用其他一些特殊方法cwrap
。
以下是我的C代码文件'fib.c'
int fib(int n)
{
if (n < 2)
return n;
return fib(n - 1) + fib(n - 2);
}
这是我的'simple_compare.html'文件
{{{ SCRIPT }}}
<input type="number" id="fibNum" value="35" />
<button id="mybutton">Compare JS vs C on Fibonacci</button>
<script>
document.getElementById("mybutton").addEventListener("click", () => {
const fibIndex = Number(document.getElementById("fibNum").value);
const t1 = executeFibonacciOnC(fibIndex);
const t2 = executeFibonacciOnJS(fibIndex);
console.log("C time:", t1, "JS time:", t2);
});
// finds the 'n'th fibonacci number in C using the worst implementation and returns the execution time
function executeFibonacciOnC(n) {
fibC = Module.cwrap("fib", "number", ["number"]);
const t1 = performance.now();
const res = fibC(n);
const t2 = performance.now();
console.log("c result: ", res);
return t2 - t1;
}
function executeFibonacciOnJS(n) {
const t1 = performance.now();
const res = fib(n);
const t2 = performance.now();
console.log("JS result: ", res);
return t2 - t1;
}
function fib(n) {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
}
</script>
在这里,您可以看到JavaScript比C代码快很多。我觉得自己一直浪费了时间,WebAssembly只是一个气球。
然后我意识到Emscripten中有一些编译器优化标志。让我们使用它们emcc -o hello4.html fib.c --shell-file html_template/simple_compare.html -sEXPORTED_FUNCTIONS=_fib -sEXPORTED_RUNTIME_METHODS=cwrap -O2
我刚刚在我的命令中添加了一个-O2
标志,然后再做一次。
现在您可以看到一些魔术!
这次,C代码执行更快!在某些情况下,它甚至快2倍!
排序数字
现在让我们尝试我们的最终目标。我们可以比JavaScript更快地对数字进行排序吗?让我们尝试使用C标准库功能qsort
我认为应该更快,C代码通常很快。与以前的命令类似,我执行了命令emcc -o qsort.html arraySorter.c --shell-file html_template/simple_compare_array.html -sEXPORTED_FUNCTIONS=_arraySorter,_malloc,_free -sEXPORTED_RUNTIME_METHODS=cwrap
我的“ arraysorter.c”非常简单。它只需使用比较功能调用qsort
函数。
#include <stdlib.h>
int compareFn(const void *a, const void *b)
{
return (*(int *)a - *(int *)b);
}
void arraySorter(int *arr, int size)
{
qsort(arr, size, sizeof(int), compareFn);
}
我的模板文件'simple_compare_array.html'如下所示。在这里传递和数组作为参数很难。我们需要使用malloc
和free
函数来创建和数组并将其传递给C。
{{{ SCRIPT }}}
<input type="number" id="arrSize" value="1000000" />
<button id="mybutton">Compare JS vs C on sorting integer array</button>
<script>
function getArray() {
const l = Number(document.getElementById("arrSize").value);
return Array.from({ length: l }, () => Math.floor(Math.random() * l));
}
document.getElementById("mybutton").addEventListener("click", () => {
const arr1 = getArray();
const arr2 = Array.from(arr1);
const t1 = arrayOperationsOnC(arr1);
const t2 = arrayOperationsOnJS(arr2);
console.log("C time:", t1, "JS time:", t2);
});
function arrayOperationsOnC(n) {
const BYTE_SIZE_OF_INT = 4;
const t1 = performance.now();
const arraySize = n.length;
const arrayPointer = Module._malloc(arraySize * BYTE_SIZE_OF_INT);
Module.HEAP32.set(new Int32Array(n), arrayPointer / BYTE_SIZE_OF_INT);
const cFunc = Module.cwrap("arraySorter", null, ["number", "number"]);
cFunc(arrayPointer, arraySize);
const resultArray = Array.from(
Module.HEAP32.subarray(
arrayPointer / BYTE_SIZE_OF_INT,
arrayPointer / BYTE_SIZE_OF_INT + arraySize
)
);
console.log(resultArray);
Module._free(arrayPointer);
const t2 = performance.now();
return t2 - t1;
}
function arrayOperationsOnJS(n) {
const t1 = performance.now();
const res = arraySorter(n);
console.log(res);
const t2 = performance.now();
return t2 - t1;
}
function arraySorter(arr) {
return arr.sort((a, b) => a - b);
}
</script>
如果我执行此操作,我会看到我的C代码速度慢了4倍!
到底有什么问题?
好吧,我发现我没有使用编译器优化标志。让我们尝试-O2
标志,然后再做一次。现在它更快,但仍然比JavaScript慢。
c时间:716.1 JS时间:269.5
即使我使用-O3
标志,我也发现它仍然较慢。没有“ -o4”这是最优化的。
c时间:711.9 JS时间:270.6
现在,我们确定使用C快速排序无法通过普通的JavaScript。但是我们使用了C标准库功能qsort
。我们可以尝试简单的C代码以进行快速排序吗?咱们试试吧。我在这里使用了命令emcc -o faster_sorter.html arraySorter2.c --shell-file html_template/simple_compare_array.html -sEXPORTED_FUNCTIONS=_arraySorter,_malloc,_free -sEXPORTED_RUNTIME_METHODS=cwrap -O2
,我使用了相同的HTML模板,但是这次我用普通的C代码实现了快速排序算法。下面在我的arraySorter2.c
文件中。
// Quick sort in C
// function to swap elements
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
// function to find the partition position
int partition(int array[], int low, int high)
{
// select the rightmost element as pivot
int pivot = array[high];
// pointer for greater element
int i = (low - 1);
// traverse each element of the array
// compare them with the pivot
for (int j = low; j < high; j++)
{
if (array[j] <= pivot)
{
// if element smaller than pivot is found
// swap it with the greater element pointed by i
i++;
// swap element at i with element at j
swap(&array[i], &array[j]);
}
}
// swap the pivot element with the greater element at i
swap(&array[i + 1], &array[high]);
// return the partition point
return (i + 1);
}
void quickSort(int array[], int low, int high)
{
if (low < high)
{
// find the pivot element such that
// elements smaller than pivot are on left of pivot
// elements greater than pivot are on right of pivot
int pi = partition(array, low, high);
// recursive call on the left of pivot
quickSort(array, low, pi - 1);
// recursive call on the right of pivot
quickSort(array, pi + 1, high);
}
}
void arraySorter(int *arr, int size)
{
quickSort(arr, 0, size - 1);
}
现在,使用-O2
标志,我们可以比普通的javascript更快地对整数排序!现在,C代码更快的速度大约2倍。太神奇了!
c时间:156.8 JS时间:271.5
WASM在您的浏览器中更快吗? Try and see现在。所有source codes均可获得MIT许可。