资讯专栏INFORMATION COLUMN

Emscripten教程之优化你的代码

bladefury / 3370人阅读

摘要:优化项也会引发一些问题。检查你的代码是否工作并修复问题。从起,及以上的优化级别默认启动了这项设置。目前正在进行改进。代码移植系列文章代码移植主题系列文章是中文站点的一部分内容。

作者:云荒杯倾
欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。


这是关于Emscripten的系列文章,更多文章请看下面链接。

Emscripten代码移植系列文章

Emscripten代码移植主题系列文章是emscripten中文站点的一部分内容。
本文是第一个主题。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍连接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统
第六个主题介绍Emscripten如何调试代码

译文地址
原文地址

通常来说,你只要编译并运行你的代码,并不需要优化。一旦你能保证代码运行正确,
你就可以使用本文提到的技术让你的代码加载和运行的更快。

1、怎么优化代码

在使用emcc时,通过指定优化标志来进行优化。优化有级别之分,分别是:-O0,-O1,
-O2,-Os,-Oz,-O3。

比如,下面代码就是使用-O2级别进行优化编译

emcc -O2 file.cpp

随着优化级别增加,渐进引入更加激进的优化方式,从而导致更高的性能和更大的代码体积
,代价是编译时间会增加。优化项也会引发一些问题。

那么,什么时候用什么级别的优化呢?

第一次移植代码时,运行emcc使用默认settings,不要优化。检查你的代码是否工作

并修复问题。

开发中为了一个较短的编译迭代周期,使用低级别的优化。即-O0或者-O1。

发布代码时,使用-O2或者-O3。-O3比-O2更加优化,不过需要明显更长的编译时间。

其他优化在下面部分讨论。

除了-Ox选项或级别对所有优化项目是共同的,js优化、llvm优化、llvm link-time优化由各自独立的编译选项来控制。

Note:
emcc优化标志的意义不同于gcc,clang,或其他编译器的相似名字的选项,因为对JS代码优化和对原生代码优化是非常不同的。
emcc的优化级别和llvm bitcode优化级别的映射关系写在reference那篇文章中。
2、高级编译器设置

你可以给编译器传一些标志进去以影响代码的生成,同事这也影响性能。比如DISABLE_EXCEPTION_CATCHING,
这些可以在settings 看到。

一些有用的标志是:

NO_EXIT_RUNTIME: 编译时使用-s NO_EXIT_RUNTIME=1 ,这样编译器就知道你不想让

程序在运行完main()函数后就结束。此时,编译器就会放弃atexit和全局析构这两个
函数的调用(如果你不设置-s NO_EXIT_RUNTIME=1,是会调用的),这样就减小了代码体积,
并且加快了启动速度。
当你的main()函数完成但是你仍想要执行代码时,这是有用的,比如在app中使用main loop funciton
.

NOTE:
如果你的C代码里面有emscripten_set_main_loop(),它被emscripten检测到的话,emscripten也不会
在main结束调用后关闭运行时,但是呢,你最好还是在编译选项中加-s NO_EXIT_RUNTIME=1吧,
毕竟这样可以减小生成不必要的代码,是不是?!
3、代码大小

本节将描述与代码大小相关的优化和问题。它们对大小项目都很有用。
在小项目或库中,您可以获得最小的封装;在大型项目中,
代码的巨大规模可能会导致您本来想要避免的问题(比如慢速启动速度)。

3.1 内存初始化

默认情况,emscripten会在.js文件中写静态内存初始化代码。这会导致js文件很大,
也会减缓启动速度。由于js引擎对数组大小的限制,这也可能导致js引擎抛出 array initializer too large
Too much recursion 两个报错。
emcc --memory-init-file 1的设置会将静态内存代码初始化的这一部分代码放到.mem后缀
的文件中,和.js文件分开。这个.mem文件会在main()函数被调用,代码执行前异步加载。

NOTE:
从Emscripten 1.21.1 起,-O2及以上的优化级别默认启动了这项设置。
3.2 代码大小和性能的平衡

你可能希望在项目中编译性能不用怎么好的源文件,那就使用-Os和-Oz吧。其他部分
使用-O2。(-Os和-Oz以性能为代码减小代码体积,他们和-O2相似)。
注意:-Oz编译比较耗时。

3.3 其他关于代码大小的小贴士(杂项)

除了3.1和3.2,下面的建议也可以减小代码大小:

这篇文章 很有帮助

从bitcode到js的编译过程使用 llvm-lto,用法:--llvm-lto 1。

不允许嵌入/内联代码:-s INLINING_LIMIT=1。

下面还有6条,不逐一翻译。

4、大型代码库优化

上面第三部分介绍的减小代码体积部分对大型代码库很有用,另外,还有一些其他对
大型代码库优化有用的主题。

4.1 分离asm.js文件避免内存峰值

默认情况下,Emscripten会编译出一个JS文件,包含整个代码库:一个是asm.js代码,
一个是运行环境、连接浏览器等胶水代码。对于一个非常大的代码库,这样搞
内存使用方面会效率低下,因为所有的代码都在一个脚本就意味着js引擎可能使用一些内存
来解析和编译asm,而且在开始运行代码库之前,这部分内存可能不会释放。在大型游戏中,
开始运行代码可能需要动态分配一个很大的类型数组作内存,因此您可能会看到内存的“峰值”,
在此之后将释放临时的编译内存(解析和编译asm那部分内存)。如果足够大,这个峰值
会导致浏览器耗尽内存,无法加载应用程序。这是Chrome的一个已知问题(其他浏览器似乎没有这个问题)。

一种方法是分离出asm。js到另一个文件,并确保浏览器在编译asm之间有一个事件循环
,保证编译asm和运行程序有一个顺序。这可以通过运行emcc --separate-asm来实现。

4.2 自运行 4.3 列提纲outlining

JavaScript引擎通常会慢慢地编译非常大的函数(相对于它们的大小),
并不能有效地(或根本)对它们进行优化。解决这个问题的方法之一是使用“列提纲”:
将它们分解成更小的函数,可以更有效地编译和优化。
“列提纲”增加了总体代码的大小,并且可以使一些代码变得不那么优化。尽管如此,
“列提纲”有时可以提高启动速度和运行时速度。
了解更多信息。

OUTLINING_LIMIT选项定义了emscripten是否会分解一个函数为众多小函数的值。
怎么选一个合适的大小值进行拆解,怎么决定哪些函数被拆解,
点这里。

4.4 激进地消除变量

激进的变量消除尝试尽可能地去删除变量,甚至愿意付出重复表达式这种方法
(会增加代码大小)的代价。如果你有非常大的函数,这可以提高速度。
例如,它可以使sqlite(它有一个巨大的解释器循环,有数千行)快7%。

5、其他优化问题
5.1 C++异常

在- o1(及以上)中默认关闭c++异常。
这防止了try - catch块的生成,它使代码运行得更快,也使得代码更小。

要在优化的代码中重新启用异常,请运行emcc命令- s DISABLE_EXCEPTION_CATCHING= 0

5.2 内存增长

使用- s ALLOW_MEMORY_GROWTH= 1命令的编译允许根据应用程序的需求改变内存总量。
这对于预先不知道需要多少内存的应用程序很有用,但是它禁用了一些优化。(目前正在进行改进。)

5.3 内联/嵌套

内联通常产生较大的函数,因为这些可以使编译器的优化更加有效。
不幸的是,大型函数在运行时比多个较小的函数要慢,因为JavaScript引擎通常
不优化大函数(因为害怕长时间JIT),或者优化它们导致明显的停顿。

NOTE:
- o1和- o2默认会内联函数。讽刺的是,在某些情况下,这实际上会降低性能!
5.4 查看“代码优化通过”记录

启用调试模式(EMCC_DEBUG)将每种通过的JavaScript优化输出到文件。

6、不安全的优化

你可能想试一些不安全的优化项:

-s FORCE_ALIGNED_MEMORY=1: 使所有内存存取完全对齐。这会破坏那些实际上

不需要内存对齐的代码。

--llvm-lto 1:可以使llvm link-time得到优化,有些情况下有用。但是他们有一些已知的

问题,所以代码必须被充分地测试。点击查看更多。

--closure 1: 可以减小胶水代码的体积,减少启动时间。但是如果你没有做

正确的closure complier,可能有问题。

7、性能分析

现代浏览器有JavaScript profiler,可以帮助查找代码中较慢的部分。
由于每个浏览器的分析器都有限制,所以建议在多个浏览器中进行分析。
为了确保已编译的代码包含足够的信息进行分析,
可以使用下面标志设定编译命令:

emcc -O2 --profiling file.cpp
8、排查不佳的性能

emscrip10编译的代码目前可以实现本地构建的大约一半的速度。
如果性能明显低于预期,您还可以运行下面的其他故障排除步骤:

构建项目是一个两阶段的过程:将源代码文件编译成LLVM,并从LLVM生成JavaScript。

您是否在两个步骤中使用相同的优化值(- o2或- o3)?

在多个浏览器上进行测试。如果在一个浏览器上性能可以接受,在另一个浏览器上

明显较差,那么可以给我们提交一个bug报告,注意写清楚出问题的浏览器和其他相关信息。

代码是否在Firefox中验证正确(在火狐控制台查看是否有:“成功编译asm.js代码”信息输出)。

如果您在使用最新版本的Firefox和Emscripten时看到验证错误,请提交一个错误报告。


Emscripten代码移植系列文章

Emscripten代码移植主题系列文章是emscripten中文站点的一部分内容。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍连接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统
第六个主题介绍Emscripten如何调试代码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/51269.html

相关文章

  • Emscripten教程优化你的代码

    摘要:优化项也会引发一些问题。检查你的代码是否工作并修复问题。从起,及以上的优化级别默认启动了这项设置。目前正在进行改进。代码移植系列文章代码移植主题系列文章是中文站点的一部分内容。 作者:云荒杯倾欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。 这是关于Emscripten的系列文章,更多文章请看下面链接。 Emscripten代码移植系列文章 Emscr...

    Jokcy 评论0 收藏0
  • Emscripten教程入门指导

    摘要:欢迎加入和技术交流群,群聊号码。下面是正文如果只是想要入门的话,使用是非常简单的。主要是通过来工作的。实际上,这块区域不仅可以显示文本。对于开发者来说,这是非常好的资源。 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作者的博客阅读文章。欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。 下面...

    jzzlee 评论0 收藏0
  • Emscripten教程入门指导

    摘要:欢迎加入和技术交流群,群聊号码。下面是正文如果只是想要入门的话,使用是非常简单的。主要是通过来工作的。实际上,这块区域不仅可以显示文本。对于开发者来说,这是非常好的资源。 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作者的博客阅读文章。欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。 下面...

    joyvw 评论0 收藏0
  • Emscripten教程入门指导

    摘要:欢迎加入和技术交流群,群聊号码。下面是正文如果只是想要入门的话,使用是非常简单的。主要是通过来工作的。实际上,这块区域不仅可以显示文本。对于开发者来说,这是非常好的资源。 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作者的博客阅读文章。欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。 下面...

    fnngj 评论0 收藏0
  • Emscripten教程代码可移植性与限制(一)

    摘要:教程之代码可移植性与限制一翻译云荒杯倾本文是专栏系列文章之一,更多文章请查看专栏。下面是正文代码可移植性与限制几乎可以编译任何可移植的代码到。如果标准机构将共享状态添加到中,支持多线程代码将成为可能。 Emscripten教程之代码可移植性与限制(一) 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作者的博客阅读文章。欢迎...

    yangrd 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<