Home > Erlang探索, 体系结构, 源码分析 > 基于LLVM的高性能Erlang(Hipe)尝鲜

基于LLVM的高性能Erlang(Hipe)尝鲜

March 25th, 2014

原创文章,转载请注明: 转载自系统技术非业余研究

本文链接地址: 基于LLVM的高性能Erlang(Hipe)尝鲜

即将发布的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
$ wget http://llvm.org/releases/3.4/llvm-3.4.src.tar.gz
$ 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] [source] [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] [source] [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] [source] [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同等性能,稳定性更好, 官方说法是可以在产品中使用。
祝玩得开心!

Post Footer automatically generated by wp-posturl plugin for wordpress.

  1. Herry
    March 28th, 2014 at 11:59 | #1

    Hi,余大
    我运行hipe:c(test, [to_llvm]).报错
    Warning: [hipe:1515]: Unknown options: [to_llvm].
    google的半天也没找到靠谱的解决办法

    Yu Feng Reply:

    最近的R17才有,也就是说最新的github上的master分支,还没有发布呢

    Herry Reply:

    多谢余大

  2. manlin
    March 29th, 2014 at 11:49 | #2

    热更新还像以前一样方便么

    Yu Feng Reply:

    没变

  3. Nobody
    April 21st, 2014 at 11:09 | #3

    余老师,麻烦请教个问题:
    Erlang支持Binary直接映射到record不?我尝试这样写过,报错,请指点,谢谢了:

    1 -module(record).
    2 -export([birthday/1, joe/0, showPerson/1]).
    3
    4 -define (INT, 32/signed-big-integer).
    5 -define (USHORT, 16/unsigned-big-integer).
    6 -define (SHORT, 16/signed-big-integer).
    7 -define (UBYTE, 8/unsigned-big-integer).
    8 -define (BYTE, 8/signed-big-integer).
    9 -define (BIN8, 8/binary).
    10 -define (BIN21, 21/binary).
    11 -define (BIN5, 5/binary).
    12 -define (BIN10, 10/binary).
    13 -define (BIN16, 16/binary).
    14 -define (BIN6, 6/binary).
    15 -define (BIN4, 4/binary).
    16
    17 -record(address, {post_addr, post_no}). -record(person, {name,age=0,phone, address}).
    18
    19 birthday(#person{age=Age} = P) ->
    20 P#person{age=Age+1}.
    21
    22 joe() ->
    23 #person{name=”Joe”,
    24 age=21,
    25 phone=”999-999″,
    26 address = #address
    27 {post_addr=”postaddr”,
    28 post_no=”post_no”}}.
    29
    30 showPerson(Person) ->
    31 io:format(“name: [~p] age: [~p] phone: [~p], postaddr:[~p], postno:[~p] ~n”,
    32 [Person#person.name,
    33 Person#person.age,
    34 Person#person.phone,
    35 Person#person.address#address.post_addr,
    36 Person#person.address#address.post_no ]).
    37
    38 joe(BinaryData)->
    >> 39 <> = BinaryData,
    44 P.
    45

    新手问题,我实在是没找到这方面的资料,想过使用临时变量存储一下,再导入到record,但是总觉的这样不是太简洁,代码有点冗余!

    Yu Feng Reply:

    record实际上是个tuple,没有binary到tuple的转换,也没必要。

  4. botanyzh
    May 17th, 2014 at 02:47 | #4

    余大 既然hipe改用 llvm 做后端了 是不是只要 llvm 能在windows 上 用 hipe就也能了呢

    Yu Feng Reply:

    windows是二等公民,高性能就算了,别用windows.

  5. pengyafu
    May 11th, 2015 at 18:48 | #5

    test.erl: Warning: this system is not configured for native-code compilation.这是cpu类型不支持native吗

    fishballian Reply:

    erlang编译的时候没有带上enable hipe

Comments are closed.