用Emscripten编译的WebAssembly可以生成更小的文件大小吗?



我对WebAssembly非常感兴趣,但令人沮丧的是,即使是用C++编码并使用Emscripten编译的"Hello World"示例,也会产生总共396KB的负载。什么给?如何提高尺寸效率?

总结

  • 对于像游戏引擎这样的大型项目,与小型 Hello World 示例相比,Emscripten 生成的代码具有成比例的较小大小开销。
  • Emscripten最近在缩小代码大小方面做了很大的改进。确保使用最新的 Emscripten 版本。
  • 添加-Os –closure 1可以将生成的代码的大小减少 10 倍。

以下是回答问题的说明how can this be made more size-efficient


为什么生成如此多的代码?

生成的 Webassembly 数量与编写C++代码的数量以及该代码的依赖关系成正比。依赖于标准库的C++程序所依赖的代码比您预期的要多。像这样的简单add()函数...

int add(int x, int y) {
return x + y;
}

..将生成一个简短的 Webassembly 函数,如下所示:

(func $add (param $x i32 $y i32) (return i32)
(get_local 0
get_local 1
i32.add))

但是对printf的调用需要对strlenflockfilefunlockfilememcpyfwritefputs__stdio_write等函数进行定义,即进行printf调用所需的标准库中的所有函数。在本机环境中运行的C++程序只会与平台的正确libc链接,但是Webassembly需要携带这些库依赖项

除了用户空间库依赖项之外,生成 Webassembly 的工具还需要提供处理系统调用的运行时环境。因此,Hello World 程序需要具有覆盖系统调用以分配内存和写入字节的定义。


编译器如何缩小代码大小?

Emscripten的创建者和维护者Alon Zakai撰写了Mozilla Hacks文章Shrinking Webassembly and Javascript代码大小。我将在这里总结该文章的要点:

Emscripten 最初专注于通过实现 libc 和系统调用运行时来提供 Posix 环境,从而简化移植现有 C 和 C++ 程序。为了方便起见,通常包含的代码多于需要的代码。

很多运行时都是作为Javascript代码实现的。Emscripten生成在应用程序/库Webassembly代码和Javascript运行时之间来回调用的代码。

应删除从未调用的代码。在编译器中,由称为死代码消除的优化处理。Emscripten 构建了所有函数的图形,并删除了那些从未从 main 调用的部分。好的,这不是严格正确的,但足以说明这一点。

但是编译器以前无法为跨越Webassembly和Javascript之间边界的调用生成这种图。随着wasm-dce工具的加入,这种情况发生了变化。现在,Emscripten可以创建Webassembly和Javascript代码的图形。


Hello World程序的"收缩"极限是多少

printf是对文件描述符进行操作的通用函数,并且是线程安全的。为 printf 调用生成的代码几乎都必须在那里。

如果您想对生成的代码进行更多实验,我推荐 Webassembly Studio 在线 IDE。它提供了一个示例 Hello World 项目,其中包含一个自述文件,其中包含生成哪些库代码和运行时 Javascript 代码。

更高的优化级别引入了逐渐更积极的优化,从而以增加编译时间为代价提高了性能和代码大小。 - 出

这就是 emscripten 文档关于缩小文件大小的说法。请注意,更高级别的优化并不意味着更小的文件大小。每个优化选项的行为都不同,这在文档中得到了很好的描述。

下面的示例使用-Os代码优化选项,该选项使编译器的行为如下所示:

与 -O3 类似,但进行了额外的优化,以牺牲性能为代价来减小代码大小。这可能会影响位码生成和 JavaScript。-源

emcc -Os file.cpp

当wasm实现DOM/web API时,您将获得更小的尺寸和更好的性能,因此您需要回调javascript。

最新更新