我对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
的调用需要对strlen
、flockfile
、funlockfile
、memcpy
、fwrite
、fputs
、__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。