Erlang
wxErlang
前回の続きでwxErlangの資料を読んで、試してみた。こういう資料が家に居ながらにして読めるって、このコロナの時代有り難い事です。
OpenBSDでもwxErlangは、いわゆるErlangとは別パッケージになってた。 Elixir入門 21: デバッグ でも取り上げられているけど、GUI系ですからねぇ。人によってはGUI拒否症の人もいるから、無理強いしませんって事だな。
頭に付くwxは色々有る。有名な所では、wxPythonとかwxMaximaとかね。x11/wxWidgetsには
wxWidgets gives you a single, easy-to-use API for writing GUI applications on multiple platforms. Link with the appropriate library for your platform (Windows/Unix/Mac, others coming shortly) and compiler (almost any popular C++ compiler), and your application will adopt the look and feel appropriate to that platform. On top of great GUI functionality, wxWidgets gives you: online help, network programming, streams, clipboard and drag and drop, multithreading, image loading and saving in a variety of popular formats, HTML viewing and printing, and much much more. This package contains the GTK3 version of the library.
こんな説明がされていた。現代風なTkと思っていればいいんだな。Cフラフラ語での提供らしい。これだけでは、みんな大好きAVには対応出来ないので、コンパニオンさんが居る。
wxMediaCtrl is a wxWidgets class for displaying types of media, such as videos, audio files, natively through native codecs.
wxWidgets-gtk3-3.0.5.1.tgz 8572658 wxWidgets-media-3.0.5.1.tgz 46479 wxchordpro-0.974.1.tgz 14698 wxglade-1.0.0.tgz 1100557 wxsvg-1.5.22v0.tgz 1300750
パッケージがこれだけある。でも、このパッケージを支える底辺にgtk3群が必要になるので、わんさかと、子分を連れてくるぞ。GUIは大飯喰らいである。
-module(hello). -include_lib("wx/include/wx.hrl"). -export([start/0]). start() -> Wx = wx:new(), Frame = wx:batch(fun() -> create_window(Wx) end), wxWindow:show(Frame), loop(Frame), wx:destroy(), ok. create_window(Wx) -> Frame = wxFrame:new(Wx, -1, % window id "Hello World", % window title [{size, {600,400}}]), wxFrame:createStatusBar(Frame,[]), wxFrame:connect(Frame, close_window), ok = wxFrame:setStatusText(Frame, "Hello World!",[]), Frame. loop(Frame) -> receive #wx{event=#wxClose{}} -> io:format("~p Closing window ~n",[self()]), ok = wxFrame:setStatusText(Frame, "Closing...",[]), wxWindow:destroy(Frame), ok; Msg -> io:format("Got ~p ~n", [Msg]), loop(Frame) end.
これがGUI版のハロワである。メニューとかが(File,Edit,…About)とかが必要になると、途端にソースの分量が増える。何時の時代もGUIは使って天国、作って地獄であるな。
compile from src
vbox$ ls -l /usr/ports/distfiles/ total 166848 -rw-r--r-- 1 root wheel 85351485 Dec 11 2018 otp_src_21.2.tar.gz
OpenBSDでは、結構昔の物が使われている。これとて、展開してインストールしようとすると、wxの所で、out of memoryエラーを喰らってしまった。clangの最適化でメモリーを大量消費してるんだろうね。ulimit を駆使して使うメモリーを増やすって手も考えてはみたんだけど、なんだかなあの気分。
どうせならerlang処女地のOpenBSD(64Bit)で、wxには目もくれずにコンパイルしてみるか。どうせなら最新式な奴が良い。んだけど、最新式なやつは、仮想マシンのネイティブをサポートしていないらしい(FreeBSDのportsからの情報)。そんなに真剣に使う訳ではないので、目をつぶろう。
普通にconfigureしてmakeしたら、ものの5分で完了。おまけで、elixirも最新式を入れてみた。
ob$ iex Erlang/OTP 23 [erts-11.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] Interactive Elixir (1.13.0-dev) - press Ctrl+C to exit (type h() ENTER for help) iex(1)>
otp_src_23.3
を入れたので、バナーでOTP 23 と言って来るのは理解出来る。erts-11.2ってのは、coreになるバージョンかな? erlang run time system とかの略だろうね。smpは4CPUで動いてますの報告だろう。次のdsは何だろう? 得意のソース嫁かな。
Eshell V11.2 (abort with ^G)
erlを単独で動かすと、eshellのバージョンも表示される。これってreplの事だよね。そこら辺も見たいと言う欲望が沸々と湧いてますよ。 ざっとMakefileを眺めた限りでは、gdb用の情報が添付されてるみたいだ。
観光
gdbにかかるか、アタッチして確認したい。だってerlはshellスクリプトだからね。
ob$ ps a PID TT STAT TIME COMMAND 77389 p2 I+ 0:00.55 /usr/local/lib/erlang/erts-11.2/bin/beam.smp -- -roo
動いているやつを追う。
ob$ gdb -q /usr/local/lib/erlang/erts-11.2/bin/beam.smp -p 77389 Reading symbols from /usr/lib/libutil.so.15.0...done. Reading symbols from /usr/lib/libm.so.10.1...done. Reading symbols from /usr/lib/libcurses.so.14.0...done. Reading symbols from /usr/lib/libm.so.10.1...done. Reading symbols from /usr/lib/libcurses.so.14.0...done. Reading symbols from /usr/lib/libc.so.96.0...done. Reading symbols from /usr/libexec/ld.so...done. [New thread 125326] : 23 threads runs [New thread 205805] [Switching to thread 202773] _thread_sys_select () at /tmp/-:3 3 /tmp/-: No such file or directory.
随分スレッドが動いている。
(gdb) bt #0 _thread_sys_select () at /tmp/-:3 #1 0x0000030f403777ee in _libc_select_cancel (nfds=0, readfds=0x0, writefds=0x0, exceptfds=0x30f4038ab1a <_thread_sys_select+10>, timeout=0x0) at /usr/src/lib/libc/sys/w_select.c:28 #2 0x0000030c7ef1d6f0 in erts_sys_main_thread () at sys/unix/sys.c:1161 #3 0x0000030c7edc098d in erl_start (argc=<optimized out>, argv=<optimized out>) at beam/erl_init.c:2376 #4 0x0000030c7ed45699 in main (argc=0, argv=0x0) at sys/unix/erl_main.c:30
スレッドスイッチの嵐に見舞われそうなんで、尻尾を巻いて逃げ出します。後で、ピンポイントで調べるかも知れないけど。
79330 p2 S 0:00.16 epmd
psした時、気になる奴を見つけた。ひょっとしてウィルスに侵されたかと冷や冷や。erlang一族だったよ。
Erlang Port Mapper Daemon This daemon acts as a name server on all hosts involved in distributed Erlang computations. When an Erlang node starts, the node has a name and it obtains an address from the host OS kernel. The daemon is started automatically by command erl(1)
debian(64Bit)に入れてgdbしたら、少しは親切にスレッドの名前が出て来た。
(gdb) info threads Id Target Id Frame * 1 Thread 0x7f37ad746b80 (LWP 62980) "beam.smp" 0x00007f37ad839037 in __GI___select (nfds=nfds@entry=0, readfds=readfds@entry=0x0, writefds=writefds@entry=0x0, exceptfds=exceptfds@entry=0x0, timeout=timeout@entry=0x0) at ../sysdeps/unix/sysv/linux/select.c:41 2 Thread 0x7f376c6bf700 (LWP 62984) "sys_sig_dispatc" __libc_read ( nbytes=4, buf=0x7f376c6bee60, fd=12) at ../sysdeps/unix/sysv/linux/read.c:26 3 Thread 0x7f376b77f700 (LWP 62985) "sys_msg_dispatc" futex_wait_cancelable (private=0, expected=0, futex_word=0x56435ab5e32c <smq_cnd+44>) at ../sysdeps/unix/sysv/linux/futex-internal.h:88 4 Thread 0x7f376c728700 (LWP 62986) "async_1" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38 5 Thread 0x7f376adff700 (LWP 62988) "1_scheduler" syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38 : 24 Thread 0x7f376a516700 (LWP 63007) "0_poller" 0x00007f37ad8417ef in epoll_wait (epfd=4, events=events@entry=0x7f376d209f38, maxevents=maxevents@entry=512, timeout=-1) at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
ここに出て来るソースは思わせぶりな奴だなあ。
eshell
erlってreplだ、じゃなくてシェッルだ。helpぐらいは有るだろう。
1> help(). ** shell internal commands ** b() -- display all variable bindings e(N) -- repeat the expression in query <N> : ** commands in module c ** bt(Pid) -- stack backtrace for a process c(Mod) -- compile and load module or file <Mod> : ** commands in module i (interpreter interface) ** ih() -- print help for the i module true
やはり有った。
5> i(). Pid Initial Call Heap Reds Msgs Registered Current Function Stack <0.0.0> otp_ring0:start/2 233 1531 0 init init:loop/1 2 <0.1.0> erts_code_purger:start/0 233 11 0 :
これって、ps相当だな。色々なプロセスが動いている。それで、スレッドが沢山って事なんだな。OS任せにしないで、自前で疑似OS風に振る舞うとな。
6> m(). Module File application /usr/local/lib/erlang21/lib/kernel-6.2/ebin/application.beam erl_tracer preloaded erlang preloaded :
全部のモジュールを列挙出来る。疑似OS上でのls相当か。ls().は、本物のOSのlsだから、混乱しないでね。
7> m(timer). Module: timer MD5: 9b3d4bac79ba3daac2e45692552fbd13 Compiled: No compile time info available Object file: /usr/local/lib/erlang21/lib/stdlib-3.7/ebin/timer.beam : kill_after/2 tc/2 kill_after/1 tc/3 terminate/2 ok
そして、個別にモジュールを指定すると、モジュール情報が出て来る。モジュール内で定義されてる関数名が出て来る事か、unixにこじつけると簡易なmanだな(余りに貧弱だけど)。細かい事は、実験しつつソース嫁がOSSと付き合う為の基本的な態度です。
Eshellのソースは、stdlib/の中に、 shell_default.erl
shell_docs.erl
shell.erl こんな3個のファイルとして置いてあるぞ。
Erlang/OTP 23.3 see Basic/stdlib
gg出来るマニュアルとかも揃っているな。
ふと思い立って、起動状態を確認してみた。長いコマンド引数なので、wideなフォーマットで、コマンドだけを表示するように指定。それでも冒頭部分は長いので省略してる。
vbox$ ps -awocommand beam.smp -- -root /usr/local/lib/erlang21 -progname erl -- -home /home/sakae -- beam.smp -- -root /usr/local/lib/erlang21 -progname erl21 -- -home /home/sakae -- -pa /EXTd/l
上段はerl(eshell)の起動。下段はelixir(iex)の起動。/EXTdは、2nd-DISKのマウント・ポイントと思われる。
vbox$ sh -x /usr/local/bin/erl + ROOTDIR=/usr/local/lib/erlang21 + BINDIR=/usr/local/lib/erlang21/erts-10.2/bin + EMU=beam + echo /usr/local/bin/erl + sed s/.*\/// + PROGNAME=erl + export EMU + export ROOTDIR + export BINDIR + export PROGNAME + exec /usr/local/lib/erlang21/erts-10.2/bin/erlexec Erlang/OTP 21 [erts-10.2] [source] [smp:1:1] [ds:1:1:10] [async-threads:1] Eshell V10.2 (abort with ^G)
EMUって、エミュレータの意味(だよね)。
vbox$ sh -x /usr/local/bin/iex + set -e + [ = --help ] + [ = -h ] + readlink_f /usr/local/bin/iex + SELF=/EXTd/local/lib/elixir/bin/iex + dirname /EXTd/local/lib/elixir/bin/iex + SCRIPT_PATH=/EXTd/local/lib/elixir/bin + exec /EXTd/local/lib/elixir/bin/elixir --no-halt --erl -noshell -user Elixir.IEx.CLI +iex Erlang/OTP 21 [erts-10.2] [source] [smp:1:1] [ds:1:1:10] [async-threads:1] Interactive Elixir (1.11.4) - press Ctrl+C to exit (type h() ENTER for help)
まて、これが正しい観測方法。
timer:tc
実行時間を計測してくれる奴。schemeだとtimeってマクロだったな。
3> timer:tc(io, format, ["~nHello World!~n", []]). Hello World! {172,ok}
定義場所を探してみたよ。 stdlib/timer.erl
%% %% Measure the execution time (in microseconds) for an MFA. %% -spec tc(Module, Function, Arguments) -> {Time, Value} when Module :: module(), Function :: atom(), Arguments :: [term()], Time :: integer(), Value :: term(). tc(M, F, A) -> T1 = erlang:monotonic_time(), Val = apply(M, F, A), T2 = erlang:monotonic_time(), Time = erlang:convert_time_unit(T2 - T1, native, microsecond), {Time, Val}.
頭がマイナスで始まっている -spec は、アトリビュートって言うらしい。 erlang:monotonic_time()
はOSが提供する各種の分解能を持った時計から、インストール時に選択されて使われるようだ。余り深入りすると苦くなりそうなので、この辺にしておく。
banner
起動時に出て来るあれ、どういう意味なの?
vbox$ erl Erlang/OTP 21 [erts-10.2] [source] [smp:1:1] [ds:1:1:10] [async-threads:1] Eshell V10.2 (abort with ^G)
OpenBSD(32Bit)のports版
sakae@pen:/tmp$ erl Erlang/OTP 23 [erts-11.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]
Debian(64Bit)の自前コンパイル版
定義はconfig時に決まるとな。
erts/emulator/beam/erl_bif_info.c
static char erts_system_version[] = ("Erlang/OTP " ERLANG_OTP_RELEASE "%s" " [erts-" ERLANG_VERSION "]" #ifndef OTP_RELEASE #ifdef ERLANG_GIT_VERSION " [source-" ERLANG_GIT_VERSION "]" #else " [source]" #endif #endif #ifdef ARCH_64 " [64-bit]" #endif " [smp:%beu:%beu]" " [ds:%beu:%beu:%beu]" #if defined(ERTS_DIRTY_SCHEDULERS_TEST) " [dirty-schedulers-TEST]" #endif " [async-threads:%d]" #ifdef HIPE " [hipe]" : "\n");
それはいいんだけど、肝心の smp:%beu の意味が分からん。
eusakae@pen:~$ man printf : %b ARGUMENT as a string with '\' escapes interpreted, except that octal escapes are of the form \0 or \0NNN
これとは違うだろうしね。
hipe
こういうのが有るそうなので、Debian(64Bit)で試してみる。 HiPE(High Performance Erlang)について
例題は何時ものやつ。
-module(fib). -export([fib/1]). -spec fib(non_neg_integer()) -> non_neg_integer(). fib(N) when N < 2 -> 1; fib(N) -> fib(N - 2) + fib(N - 1).
まずは普通に。
3> c(fib). {ok,fib} 4> timer:tc(fib, fib, [40]). {3681145,165580141}
3.6秒かかった。
6> c(fib, [native]). {ok,fib} 7> timer:tc(fib, fib, [40]). {998711,165580141}
hipeを有効にするおまじないnativeを付けると、1秒を切った。
説明によると功罪が有るので、数値計算ぐらいに留めておいた方が良さそう。