即将发布的R17A版本引入很重要的一个针对性能提升的特性:”Support the LLVM backend in HiPE”,具体改变参见这里. 我们知道Erlang是一门领域语言,第一天就是为电信工业高可用,集群和热更新环境而设计的,语言的性能一开始不是重点。直到R12版本才加入SMP多处理器,充分适应多核化的硬件发展趋势,从此向着高性能大步迈进。

Erlang的虚拟机是register based的,性能上和python类似,和c语言大概有7倍的差距。虽然大部分的集群和网络服务器,性能瓶颈在IO上面,而且这块erts(erlang运行期系统)做的非常的强大,但是一旦涉及到大量的计算,就有点麻烦了,因为它缺乏类似java jit那样强大的支持,让语言足够的快。解决方案是自己写nif、driver或者bif,但是会破坏稳定性。

它很早有自己的hipe, 主要是Uppsala University大学的Kostis Sagonas带领学生做的, 97年开始做的,性能的提升虽然不少,但是在架构上有些缺点,而且和otp团队是二个不同的团队,在稳定性上无法达到产品质量。为了进一步解决这个问题,他带着Christos Stavrakakis和Yiannis Tsiouris,重新实现了基于LLVM后端的Hipe,也就是erllvm,官方网站在这里.

官方描述如下:

ErLLVM is a project aiming at providing multiple back ends for the High Performance Erlang (HiPE) with the use of the LLVM infastructure.

这次R17发布就是把ErLLVM融入到erlang主干版本去。那么ErLLVM的技术改进点在哪里?看下面的图就明白了。

722x227-hipe_llvm_arch

最关键的一点就是之前的hipe自己从RTL生成硬件代码,而ErlLvm把这个事情交给了llvm专业去生成,它只做RTL->llvm层的薄薄的翻译,这样稳定性的问题就offload交给了llvm,而llvm的稳定性是经过社区规模考验的。

llvm_pipeline

这样就很好的解决了稳定性和性能的问题。

作者在erlang factory上有个演讲, 我们摘抄下性能数字:

和原生的beam相比:
beam_erllvm

和最初的hipe相比:
hipe_erllvm

我们可以看到和原来的hipe性能相当,但是稳定性提升不少。接下来我们来体验下,安装指南在这里。
首先需要安装llvm, 下载页面在这里,只需要安装3.4版本以上就好。

NOTE: A custom LLVM version is not needed anymore in order to install and use the LLVM backend for HiPE. You can safely ignore everything below by making sure that you have LLVM 3.4 (or newer) installed in your system.

我的系统是RHEL6U2, 安装过程如下:

uname -r
2.6.32-220.23.1.tb704.el6.x86_64
tar xzvf llvm-3.4.src.tar.gz
cd llvm-3.4
mkdir build && cd build
$ ../configure && make -j 16
sudo make install

如果不出意外,llvm就安装好了,如何确认呢?
$ which llc
/usr/local/bin/llc

能顺利看到llc就好了, 说明安装成功,接下来安装erlang.

erlang我们很熟悉了,我就不啰嗦了,用kerl快速搞起,为了看到效果,我们把lib/hipe/llvm/hipe_llvm_main.erl里面的io:format打开方便调试。

$ kerl build git git://github.com/erlang/otp master r17alph
$ kerl install r17alph r17alph
$ r17alph/bin/erl
Erlang/OTP 17 [RELEASE CANDIDATE 2] [erts-6.0] 1 [64-bit] [smp:16:16] [async-threads:10] [hipe] [kernel-poll:false]
 
Eshell V6.0  (abort with ^G)
1>

看版本号就知道我们安装完毕了,现在来把玩下:

cat test.erl
-module(test).
-export([hello/1]).
hello(Name) ->
  io:format("Hello ~w!~n", [Name]).
CTRL+D
 
$ r17alph/bin/erl
Erlang/OTP 17 [RELEASE CANDIDATE 2] [erts-6.0] 1 [64-bit] [smp:16:16] [async-threads:10] [hipe] [kernel-poll:false]
 
Eshell V6.0  (abort with ^G)
1> c(test).
{ok,test}
2> hipe:c(test, [to_llvm]).
OPT: opt -O3 -mem2reg -strip /dev/shm/llvm_319827/module_info_0.ll -o /dev/shm/llvm_319827/module_info_0.bc
OPT: opt -O3 -mem2reg -strip /dev/shm/llvm_107590586/module_info_1.ll -o /dev/shm/llvm_107590586/module_info_1.bc
OPT: opt -O3 -mem2reg -strip /dev/shm/llvm_128365190/hello_1.ll -o /dev/shm/llvm_128365190/hello_1.bc
LLC: llc -O3 -code-model=medium -stack-alignment=8 -tailcallopt -filetype=asm /dev/shm/llvm_319827/module_info_0.bc
LLC: llc -O3 -code-model=medium -stack-alignment=8 -tailcallopt -filetype=asm /dev/shm/llvm_107590586/module_info_1.bc
LLC: llc -O3 -code-model=medium -stack-alignment=8 -tailcallopt -filetype=asm /dev/shm/llvm_128365190/hello_1.bc
gcc: gcc -c /dev/shm/llvm_319827/module_info_0.s -o /dev/shm/llvm_319827/module_info_0.o
gcc: gcc -c /dev/shm/llvm_107590586/module_info_1.s -o /dev/shm/llvm_107590586/module_info_1.o
gcc: gcc -c /dev/shm/llvm_128365190/hello_1.s -o /dev/shm/llvm_128365190/hello_1.o
{ok,test}
3>  test:hello(world).
Hello world!
ok
4> test:module_info().
[{exports,[{hello,1},{module_info,0},{module_info,1}]},
 {imports,[]},
 {attributes,[{vsn,[66993814452650317180827424605876502141]}]},
 {compile,[{options,[]},
           {version,"5.0"},
           {time,{2014,3,25,12,24,14}},
           {source,"/home/chuba/test.erl"}]}]
5>
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution

上面的例子我们演示了,先编译成普通的beam文件再通过hipe转成jit的例子,程序运行正常。同时我们也很清楚的看到llvm的工具链如何在后台生成代码。

接着我们演示下直接编程成hipe格式的:

$ r17alph/bin/erlc +native +"{hipe, [to_llvm]}"  test.erl
OPT: opt -O3 -mem2reg -strip /dev/shm/llvm_117655405/module_info_1.ll -o /dev/shm/llvm_117655405/module_info_1.bc
OPT: opt -O3 -mem2reg -strip /dev/shm/llvm_37062758/module_info_0.ll -o /dev/shm/llvm_37062758/module_info_0.bc
OPT: opt -O3 -mem2reg -strip /dev/shm/llvm_87781561/hello_1.ll -o /dev/shm/llvm_87781561/hello_1.bc
LLC: llc -O3 -code-model=medium -stack-alignment=8 -tailcallopt -filetype=asm /dev/shm/llvm_117655405/module_info_1.bc
LLC: llc -O3 -code-model=medium -stack-alignment=8 -tailcallopt -filetype=asm /dev/shm/llvm_37062758/module_info_0.bc
LLC: llc -O3 -code-model=medium -stack-alignment=8 -tailcallopt -filetype=asm /dev/shm/llvm_87781561/hello_1.bc
gcc: gcc -c /dev/shm/llvm_37062758/module_info_0.s -o /dev/shm/llvm_37062758/module_info_0.o
gcc: gcc -c /dev/shm/llvm_117655405/module_info_1.s -o /dev/shm/llvm_117655405/module_info_1.o
gcc: gcc -c /dev/shm/llvm_87781561/hello_1.s -o /dev/shm/llvm_87781561/hello_1.o
 
$ r17alph/bin/erl
Erlang/OTP 17 [RELEASE CANDIDATE 2] [erts-6.0] 1 [64-bit] [smp:16:16] [async-threads:10] [hipe] [kernel-poll:false]
 
Eshell V6.0  (abort with ^G)
1> test:module_info().
[{exports,[{hello,1},{module_info,0},{module_info,1}]},
 {imports,[]},
 {attributes,[{vsn,[66993814452650317180827424605876502141]}]},
 {compile,[{options,[{outdir,"/home/chuba"},
                     {hipe,[to_llvm]},
                     native]},
           {version,"5.0"},
           {time,{2014,3,25,12,25,2}},
           {source,"/home/chuba/test.erl"}]}]
2>
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution
 
$ r17alph/bin/erl -noshell -s test hello world  -s erlang halt
Hello [world]!

有兴趣的同学可以深入测试下性能和稳定性的提升,分享出来。

小结: ErLLVM同等性能,稳定性更好, 官方说法是可以在产品中使用。
祝玩得开心!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐