Skip to content

Instantly share code, notes, and snippets.

@sile
Last active August 29, 2015 14:10
Show Gist options
  • Save sile/b9a7c1ccee028f6cce5a to your computer and use it in GitHub Desktop.
Save sile/b9a7c1ccee028f6cce5a to your computer and use it in GitHub Desktop.
HiPE(High Performance Erlang)について ref: http://qiita.com/sile/items/d750019fcab961bbb0a7
%% 一応、gb_trees以外にも使えるようになっている
-module(bench).
-export([do_bench/5]).
%% @doc マップ(的なデータ構造)の構築時間と検索時間を測定する
do_bench(MapInit, StoreFun, FindFun, Input, Keys) ->
erlang:garbage_collect(),
{StoreTime, Map} =
timer:tc(fun () -> store_loop(MapInit, StoreFun, Input) end),
erlang:garbage_collect(),
{FindTime, _} =
timer:tc(fun () -> find_loop(Map, FindFun, Keys) end),
[
{store_time, StoreTime / (1000 * 1000)},
{find_time, FindTime / (1000 * 1000)}
].
store_loop(Map, _, []) ->
Map;
store_loop(Map, StoreFun, [{K, V} | List]) ->
store_loop(StoreFun(K, V, Map), StoreFun, List).
find_loop(_, _, []) ->
ok;
find_loop(Map, FindFun, [K | List]) ->
FindFun(K, Map),
find_loop(Map, FindFun, List).
%% かなり適当な実装のechoサーバプロセス
-module(echo).
-export([start/0, call/2, call_n/3]). % external functions
-export([loop/0]). % internal functions
%% @doc echoサーバを起動する
-spec start() -> {ok, pid()}.
start() ->
{ok, spawn(?MODULE, loop, [])}.
%% @doc echoサーバにリクエストを投げる
-spec call(Server::pid(), Request::term()) -> Response::term().
call(Server, Request) ->
Server ! {self(), Request},
receive
Response -> Response
end.
%% @doc echoサーバに`Count'回だけリクエストを投げる
-spec call_n(Server::pid(), Request::term(), Count::non_neg_integer()) -> ok.
call_n(_Server, _Request, 0) ->
ok;
call_n(Server, Request, Count) ->
Request = call(Server, Request),
call_n(Server, Request, Count - 1).
-spec loop() -> no_return().
loop() ->
receive
{From, Request} ->
From ! Request, % 送られてきたリクエストをそのまま返す
?MODULE:loop()
end.
#include <stdio.h>
#include <stdlib.h>
int fib(int n) {
if (n < 2) {
return 1;
}
return fib(n - 2) + fib(n - 1);
}
int main(int argc, char ** argv) {
int n = atoi(argv[1]);
printf("%d: %d\n", n, fib(n));
return 0;
}
-module(fib).
-export([fib/1]).
fib(N) when not is_integer(N) ->
error(badarg, [N]); % 引数の型が不正ならbadarg例外を投げるように変更
fib(N) when N < 2 ->
1;
fib(N) ->
fib(N - 2) + fib(N - 1).
-module(fib2).
-export([fib2/1]).
fib2(N) -> fib:fib(N) + fib:fib(N).
# ソースコードの取得
$ wget http://www.erlang.org/download/otp_src_17.3.tar.gz
$ tar zxf otp_src_17.3.tar.gz
$ cd otp_src_17.3
# '--enable-hipe'を指定してビルド&インストール
$ ./configure --enable-hipe ...他のオプションは省略...
$ make
$ sudo make install
# 起動: 起動メッセージに'[hipe]'という文言が含まれていたらHiPEが有効(利用可能)になっている
$ erl
Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V6.2 (abort with ^G)
1> q().
# 最適化なし
$ gcc -o fib fib.c
$ time ./fib 40
40: 165580141
real 0m0.678s
user 0m0.676s
sys 0m0.000s
# 最適化あり
$ gcc -O3 -o fib fib.c
$ time ./fib 40
40: 165580141
real 0m0.287s
user 0m0.286s
sys 0m0.000s
%% HiPEなし
> c(echo).
> {ok, Pid} = echo:start(). % 起動
> echo:call(Pid, hello). % 動作確認
hello
> Time(fun () -> echo:call_n(Pid, hello, 1000000) end). % 100万回のリクエストを処理するまでに掛かる時間
0.746382 % 0.74秒
> exit(Pid, kill), f(Pid). % 後始末
%% HiPEあり
> c(echo, [native]).
> {ok, Pid} = echo:start(). % 起動
> Time(fun () -> echo:call_n(Pid, hello, 1000000) end). % 100万回のリクエストを処理するまでに掛かる時間
0.739132 % 0.73秒
> exit(Pid, kill), f(Pid). % 後始末
%% 下準備:
%% $ cp ${OTP_17_3_SOURCE}/lib/stdlib/src/gb_trees.erl .
> code:unstick_mod(gb_trees). % 標準モジュールを上書き可能にする
true
> c(bench, [native]).
{ok, bench}.
> Input = [{random:uniform(1000000), N} || N <- lists:seq(1, 200000)]. % 入力データを20万個用意する
> Keys = [K || {K, _} <- Input]. % 検索に使用するキーセット (成功探索のみ)
%% HiPEなし
> c(gb_trees).
> bench:do_bench(gb_trees:empty(), fun gb_trees:enter/3, fun gb_trees:lookup/2, Input, Keys).
[{store_time,0.489443}, % 構築時間: 0.48秒
{find_time, 0.169788}] % 検索時間: 0.16秒
%% HiPEあり
> c(gb_trees, [native]).
> bench:do_bench(gb_trees:empty(), fun gb_trees:enter/3, fun gb_trees:lookup/2, Input, Keys).
[{store_time,0.29301}, % 構築時間: 0.29秒
{find_time,0.107849}] % 検索時間: 0.10秒
$ erl
Erlang R16B03 (erts-5.10.4) [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V5.10.4 (abort with ^G)
%% 上でHiPEコンパイルされていたfibモジュールをロードする
1> l(fib).
{module,fib}
%% HiPEの互換性が無い、というエラーメッセージが表示される
=INFO REPORT==== 2-Dec-2014::02:44:44 ===
<HiPE (v 3.10.2.2)> Warning: not loading native code for module fib: it was compiled for an incompatible runtime system; please regenerate native code for this runtime system
2> code:is_module_native(fib).
false % HiPEは有効になっていない
3> fib:fib(10).
89 % 実行することは可能
# OTP17-3のgb_treesモジュールの例
# 行数
$ wc -l gb_trees.erl
553 gb_trees.erl
# erlcコマンド自体の実行時間
$ time erlc
real 0m0.190s
user 0m0.023s
sys 0m0.239s
# HiPEなしのコンパイル時間
$ time erlc gb_trees.erl
real 0m0.419s % 0.419 - 0.190 = 0.228s
user 0m0.108s
sys 0m0.483s
# HiPEありのコンパイル時間
real 0m0.907s % 0.907 - 0.190 = 0.717s
user 0m0.495s
sys 0m1.065s
# HiPEなし
$ erlc gb_trees.erl
$ du -h gb_trees.beam
8.0K gb_trees.beam
# HiPEあり
$ erlc +native gb_trees.erl
$ du -h gb_trees.beam
28K gb_trees.beam
%% dictなら成功
> code:unstick_mod(dict).
> l(dict).
> hipe:c(dict).
{ok, dict}.
%% cryptoなら失敗
> l(crypto).
> hipe:c(crypto).
<HiPE (v 3.11)> EXITED with reason {'trans_fun/2',on_load} @hipe_beam_to_icode:1174
=ERROR REPORT==== 2-Dec-2014::04:02:45 ===
Error in process <0.120.0> with exit value: {{badmatch,{'EXIT',{{hipe_beam_to_icode,1174,{'trans_fun/2',on_load}},[{hipe_beam_to_icode,trans_fun,2,[]},{hipe_beam_to_icode,trans_fun,2,[]},{hipe_beam_to_icode,trans_mfa_code,5,[]},{hipe_beam_to_icode,trans_beam_function_chunk...
** exception exit: {{badmatch,{'EXIT',{{hipe_beam_to_icode,1174,
{'trans_fun/2',on_load}},
[{hipe_beam_to_icode,trans_fun,2,[]},
{hipe_beam_to_icode,trans_fun,2,[]},
{hipe_beam_to_icode,trans_mfa_code,5,[]},
{hipe_beam_to_icode,trans_beam_function_chunk,2,[]},
{hipe_beam_to_icode,'-module/2-lc$^1/1-1-',2,[]},
{hipe_beam_to_icode,'-module/2-lc$^1/1-1-',2,[]},
{hipe,get_beam_icode,4,[]},
{hipe,'-run_compiler_1/3-fun-0-',4,[]}]}}},
[{hipe,get_beam_icode,4,[]},
{hipe,'-run_compiler_1/3-fun-0-',4,[]},
{erl_pp,attribute,2,[]}]}
in function hipe:run_compiler_1/3
in call from hipe:run_compiler/4
in call from hipe:c/3
...以下略...
%% HiPEなし
> c(hoge).
{ok, hoge}.
> code:is_module_native(hoge). % code:is_module_native/1で判定可能
false
> hoge:hoge().
hoge
%% HiPEあり
> c(hoge, [native]).
{ok, hoge}.
> code:is_module_native(hoge).
true
> hoge:hoge().
hoge
> c(fib).
> c(fib2).
> fib:fib(10).
89
> fib2:fib2(10).
178
%% 例外発生: HiPEなし
> c(fib).
> try fib2:fib2(xxx) catch error:badarg -> erlang:get_stacktrace() end.
[{fib,fib,[xxx], % 例外送出元関数の引数の情報が載る
[{file,"fib.erl"},{line,8}]}, % 行番号もつく
{fib2,fib2,1,[{file,"fib2.erl"},{line,6}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,661}]},
{erl_eval,try_clauses,8,[{file,"erl_eval.erl"},{line,891}]}]
%% 例外発生: HiPEあり
> c(fib, [native]).
[{fib,fib,1,[]}, % 引数の数(arity)だけしか情報がない
{erl_pp,attribute,2,[]}, % fib2:fib2/1を経由しているという情報も消えている
{shell,exprs,7,[]},
{shell,eval_exprs,7,[]},
{shell,eval_loop,3,[]},
{erl_pp,attribute,2,[]}]
%% fibモジュールをHiPEなしでコンパイル
> c(fib).
{ok,fib}
%% recon_traceを使ってトレース
> recon_trace:calls({fib, fib, fun (_) -> return_trace() end}, 10).
1 % トレース対象の関数の数
> fib:fib(10).
89
4:46:10.951606 <0.33.0> fib:fib(10) % トレース結果が表示される
4:46:10.957504 <0.33.0> fib:fib/1 --> 89
%% fibモジュールをHiPEありでコンパイル
> c(fib, [native]).
{ok,fib}.
%% recon_traceを使ってトレース
> recon_trace:calls({fib, fib, fun (_) -> return_trace() end}, 10).
0 % トレース対象が存在しない
> fib:fib(10).
89 % 実行しても特に何も表示されない
%% 'o0'から'o3'まで最適化レベルを指定可能 (それぞれの詳細は後述)
> c(hoge, [native, {hipe, [o0]}]). % 最適化なし
> c(hoge, [native, {hipe, [o1]}]).
> c(hoge, [native, {hipe, [o2]}]). % デフォルト
> c(hoge, [native, {hipe, [o3]}]). % 最高レベル
> c(hoge, [native, {hipe, [to_llvm]}]). % 'to_llvm'オプションを指定する
> hipe:module_info(exports).
[{load,1},
{c,1},
{c,2},
{f,1},
{f,2},
{compile,1},
{compile,2},
{compile_core,4},
{file,1},
{file,2},
{version,0},
{help,0},
{help_hiper,0},
{help_options,0},
{help_option,1},
{help_debug_options,0},
{llvm_support_available,0},
{module_info,0},
{module_info,1},
{compile,4}]
%%
%% 全体ヘルプ
%%
> hipe:help().
The HiPE Compiler (Version 3.11)
The normal way to native-compile Erlang code using HiPE is to
include `native' in the Erlang compiler options, as in:
1> c(my_module, [native]).
Options to the HiPE compiler must then be passed as follows:
1> c(my_module, [native,{hipe,Options}]).
Use `help_options()' for details.
Utility functions:
help()
Prints this message.
help_options()
Prints a description of options recognized by the
HiPE compiler.
help_option(Option)
Prints a description of that option.
help_debug_options()
Prints a description of debug options.
version() ->
Returns the HiPE version as a string'.
For HiPE developers only:
Use `help_hiper()' for information about HiPE's low-level interface
ok
%%
%% HiPE用のコンパイルオプションの説明
%%
> hipe:help_options().
HiPE Compiler Options
Boolean-valued options generally have corresponding aliases `no_...',
and can also be specified as `{Option, true}' or `{Option, false}.
General boolean options:
[debug,load,pp_asm,pp_beam,pp_icode,pp_native,pp_rtl,time,timeout,verbose].
Non-boolean options:
o#, where 0 =< # =< 3:
Select optimization level (the default is 2).
Further options can be found below; use `hipe:help_option(Name)' for details.
Aliases:
pp_all = [pp_beam,pp_icode,pp_rtl,pp_native],
pp_sparc = pp_native,
pp_x86 = pp_native,
pp_amd64 = pp_native,
pp_ppc = pp_native,
o0, % o0〜o3までの最適化オプションの実態もここで分かる
o1 = [inline_fp,pmatch,peephole],
o2 = [icode_range,icode_ssa_const_prop,icode_ssa_copy_prop,icode_type,
icode_inline_bifs,rtl_lcm,rtl_ssa,rtl_ssa_const_prop,spillmin_color,
use_indexing,remove_comments,concurrent_comp,binary_opt] ++ o1,
o3 = [{regalloc,coalescing},icode_range] ++ o2.
ok
%%
%% 個別オプションの詳細表示
%%
> hipe:help_option(icode_range).
icode_range - Performs integer range analysis on the Icode level
ok
> hipe:help_option(to_llvm).
This is an alias for: [to_llvm,{llvm_opt,o3},{llvm_llc,o3}].
ok
%% まずバイトコードコンパイル
> c(hoge).
{ok, hoge}.
%% 下記コマンドを別のシェルで実行
%% $ mv hoge.erl h.erl
> ls("hoge.beam").
hoge.beam
ok
> ls("hoge.erl").
no such file or directory
ok
%% hipeモジュールを直接使ってHiPEコンパイル
> hipe:c(hoge).
{ok, hoge}. % beamファイルだけでコンパイルが可能
%% 通常のコンパイル関数経由でHiPEコンパイル
> c(hoge, [native]).
hoge.erl: no such file or directory
error % ソースファイルがないと怒られる
%% 処理時間計測用の補助関数を準備
> Time = fun (Fun) -> {MicroSeconds, _} = timer:tc(Fun), MicroSeconds / (1000 * 1000) end.
#Fun<erl_eval.6.90072148>
%% N=40の場合のフィボナッチ数を求めるのに掛かる時間を計測する
> fib:fib(40).
165580141
%% HiPEなし
> c(fib).
> Time(fun () -> fib:fib(40) end).
5.325893 % 5.32秒
%% HiPEあり: デフォルトオプション
> c(fib, [native]).
> Time(fun () -> fib:fib(40) end).
1.167685 % 1.16秒
%% HiPEあり: 最適化レベル最高
> c(fib, [native, {hipe, [o3]}]).
> Time(fun () -> fib:fib(40) end).
1.166181 % 1.16秒 (今回のケースでは、デフォルト(o2)とほとんど差異なし)
%% HiPEあり: LLVMバックエンド
> c(fib, [native, {hipe, [to_llvm]}]).
> Time(fun () -> fib:fib(40) end).
1.076048 % 1.07秒
%% サンプルモジュール
-module(hoge).
-export([hoge/0]).
hoge() -> hoge.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment