Julia
前回からJuliaにも登場願ってる。最近PythonやRとの比較で、よくJuliaがあちこちに登場 するな。 どうすればPythonをJuliaと同じくらい速く動かせるのか? とか、意識されてるしね。 遅れないように再度、お勉強。資料は下記ぐらいかな。
Basic Numerical Programming in Julia
Juliaの起動は速いのか?
案内によるとv0.4になって速くなったとの事。本当か? ハロワの表示で確認。
[sakae@fedora plt]$ time julia t.jl hellow real 0m5.347s user 0m0.295s sys 0m0.637s [sakae@fedora plt]$ time julia t.jl hellow real 0m0.583s user 0m0.302s sys 0m0.266s
一回目は、Fedora23の起動直後。バッファーキャッシュにjuliaが載っていないので、 Diskからエッチラコッチラとロードしてきて、リンカーがライブラリーを結合して、JITを 走らせてってやるから時間がかかる。2回目からは、キャッシュに載ってるんで、その 工程が省かれるんで速くなる。
バッファーキャッシュを任意の時点でクリアーする方法ってあるんだろうか? 調べたら、よく忘れるんでメモしてた方がおられた。 Fedora20: メモリキャッシュのクリア
[sakae@fedora plt]$ sudo sysctl -w vm.drop_caches=3 vm.drop_caches = 3 [sakae@fedora plt]$ time julia t.jl hellow real 0m5.446s user 0m0.245s sys 0m0.622s [sakae@fedora plt]$ time julia t.jl hellow real 0m0.584s user 0m0.294s sys 0m0.275s
こちらは、メモリーの状態。
[sakae@fedora plt]$ free -h total used free shared buff/cache available Mem: 755M 77M 521M 1.0M 156M 646M Swap: 1.2G 0B 1.2G [sakae@fedora plt]$ sudo sysctl -w vm.drop_caches=3 vm.drop_caches = 3 [sakae@fedora plt]$ free -h total used free shared buff/cache available Mem: 755M 76M 571M 1.0M 107M 647M Swap: 1.2G 0B 1.2G
隠れたRAM-Diskって趣きだな。威力あるよ。あれ? juliaって起動時にJITを働かせて いるの? 大きな数の素因数分解をやってみる。
[sakae@fedora plt]$ time julia t.jl Dict(5=>1,3=>2,13=>1,2207=>1,751=>1) real 0m7.208s user 0m1.978s sys 0m0.792s [sakae@fedora plt]$ time julia t.jl Dict(5=>1,3=>2,13=>1,2207=>1,751=>1) real 0m2.756s user 0m2.370s sys 0m0.328s
ハロワの初回は5.5秒だったのが、コンパイルが難しそうなやつは、余計に時間がかかって いる。一度JITでコンパイルしたやつは、juliaがキャッシュしてくれれば良いと思われ。 そんな機構があればいいのに。
時間測定
かかった時間をjuliaの中で測定するには、@time系マクロで決定なんだけど、一つ不満がある。 手頃に測定するには、関数にするしかない。ここから、あそこまでを実行する時間を 測りたいって事も時には有るだろう。
そういう時の為にストップウォッチが用意されてる。ticでスタート。tocまたはtoqで、 かかった時間を表示。好きな所に埋め込めるぞ。
julia> tic() 0x000002bc1fc76c0a julia> toc() ## or toq() elapsed time: 3.988518331 seconds 3.988518331
edit
前回 editでemacsが起動しない不具合が有った。難で? こういう時はソース嫁。特徴的な 文字列、EDITOR を手がかりにbaseの下を探ってみると、interactiveutil.jl に答えは有った。
function editor() if OS_NAME == :Windows || OS_NAME == :Darwin default_editor = "open" elseif isreadable("/etc/alternatives/editor") default_editor = "/etc/alternatives/editor" else default_editor = "emacs" end # Note: the editor path can include spaces (if escaped) and flags. command = shell_split(get(ENV,"JULIA_EDITOR", get(ENV,"VISUAL", get(ENV,"EDITOR", default_editor)))) isempty(command) && error("editor is empty") return command end
使えそうなeditorをあぶり出し、無ければemacsを仮に指定。環境変数を見て、最終的に決定って 戦略。emacsのユーザーは何も設定しなくても採用されるとな。
function edit(file::AbstractString, line::Integer) command = editor() name = basename(first(command)) issrc = length(file)>2 && file[end-2:end] == ".jl" if issrc f = find_source_file(file) f !== nothing && (file = f) end const no_line_msg = "Unknown editor: no line number information passed.\nThe method is defined at line $line." if startswith(name, "emacs") || name == "gedit" spawn(`$command +$line $file`) elseif name == "vi" || name == "vim" || name == "nvim" || name == "mvim" || name == "nano" run(`$command +$line $file`) elseif name == "textmate" || name == "mate" || name == "kate" spawn(`$command $file -l $line`) :
そして、決定したeditorによって起動方法を選んでる。 このコードを見てemacsが表示されない原因が分かった。GUI版のemacsを起動すんのね。でも、 おいらの環境ではたまたまWindows側にXmingを用意して無かったので、emacsの起動に失敗したとな。
原因が分かればXmingを面倒だけど起動しとくだけ。だが、背景が白いEmacsは嫌いってんで、何時も画面 反転の-rを付けて起動してる。このスイッチが指定出来ないんじゃ眼に悪い。しょうがないので、vimで色付きにするか。 julia-vim ああ、init.elでGUI環境か見て、 背景を黒くするってのは、オイラーの信義に反するからヤリません。
でもどうしても使い慣れたemacsが良いな。emacsを端末に貼り付けるaliasを使っているんで、 それをshファイルにして、nanoとかって名前にすれば、juliaを騙せる。が、そんな事をしたら、 オイラーの美的感覚が文句を言う。ここはどうしても、julia内で片付けてみろ。
#= This file is a part of Julia. License is MIT: http://julialang.org/license porting from interactiveutil.jl Usage: @em function or file =# import Base: find_in_path, find_source_file, function_module, functionloc function emacs(file::AbstractString, line::Integer) issrc = length(file)>2 && file[end-2:end] == ".jl" if issrc f = find_source_file(file) f !== nothing && (file = f) end run(`emacsclient -nw -a "" +$line $file`) nothing end function emacs(m::Method) tv, decls, file, line = arg_decl_parts(m) emacs(string(file), line) end emacs(file::AbstractString) = emacs(file, 1) emacs(f) = emacs(functionloc(f)...) emacs(f, t::ANY) = emacs(functionloc(f,t)...) emacs(file, line::Integer) = emacs(file, line) macro em(f) return :( emacs($f) ) end
オリジナルのコードを引っ張ってきて、editをemacsに改名し整理した。 マルチメソッドってclojureやCフラフラの専売特許かと思ったら、juliaでもちゃんと 使われているね。
後は、このコードを、~/.juliarc.jl としておけば、起動直後からemacsで編集出来る。 一応、使い方
julia> @em "base64.jl" julia> @em factor julia> @em find ERROR: function has multiple methods; please specify a type signature in functionloc at reflection.jl:334 in emacs at /home/sakae/.juliarc.jl:25 julia> methods(find) # 6 methods for generic function "find": find(testf::Function, A::AbstractArray{T,N}) at array.jl:765 find(B::BitArray{N}) at bitarray.jl:1420 find(A::Union{DenseArray{T,N},SubArray{T,N,A<:DenseArray{T,N},I<:Tuple{Vararg{Union{Colon,Int32,Range{Int32}}}},LD}}) at array.jl:777 find(x::Number) at array.jl:789 find(testf::Function, x::Number) at array.jl:790 find(S::SparseMatrixCSC{Tv,Ti<:Integer}) at sparse/sparsematrix.jl:407 julia> emacs(find,(Number,))
ファイルや普通のfunctionは問題ないけど、マルチメソッドはエラーになる。しょうがないので、どんなのが あるか、あらかじめ調べてから、emacs形式で呼ぶ。
interactiveutil.jl から発見せしもの
コードをブラウジングしてると、いろいろな物に当る。犬も歩けば棒に当るってね。 面白いものを発見。
julia> versioninfo() Julia Version 0.4.3 Commit a2f713d* (2016-01-12 21:37 UTC) Platform Info: System: Linux (i686-redhat-linux) CPU: Intel(R) Celeron(R) CPU 900 @ 2.20GHz WORD_SIZE: 32 BLAS: libopenblas (DYNAMIC_ARCH NO_AFFINITY Penryn) LAPACK: libopenblasp.so.0 LIBM: libopenlibm LLVM: libLLVM-3.3
この結果を出すのは恥ずかしいぐらい旧式の石だな。だから旧石器時代って言うんだよ。 何時頃か年代鑑定したら、2009年の暮れに購入って 記録が出てきた。丸6年の酷使にもかかわらずに無傷で動いております。
julia> whos() @em 181 bytes Function Base 19261 KB Module Core 2223 KB Module Main 21301 KB Module ans 19 bytes ASCIIString emacs 2607 bytes Function
そして、これが現在のreplの状況とな。ansってreplでの最後の結果をbindしてるんだな。
v0.4
大してJuliaを使っている訳でもないのに偉そうに、 Julia v0.4 新機能\&変更点まとめ なんてのを見ています。
で、気になったのがモジュールのコンパイル機能。この際だからGastonの盛大な、 ここ直せを直しましたよ。
WARNING: Base.String is deprecated, use AbstractString instead. likely near /home/sakae/.julia/v0.4/Gaston/src/gaston_config.jl:173
そして、
__precompile__()
をGaston.jlのmojule宣言の前に追加。
[sakae@fedora ~]$ julia -e 'tic(); using Gaston; toc()' elapsed time: 2.846259885 seconds [sakae@fedora ~]$ julia -e 'tic(); using Gaston; toc()' elapsed time: 10.55525993 seconds [sakae@fedora ~]$ julia -e 'tic(); using Gaston; toc()' elapsed time: 0.798265587 seconds
上段は、コンパイル指定せず。中段はプリコンパイル中、下段でコンパイル済みを使って くれたので、ロードが速くなった。これ便利だな。
lazy
クリスマス日記に、 Julia の Macro と Iteration の実験 なんてのが掲載されてた。Macroでピピーンと反応するのは、(隠れ)Lisperの性。
題材はLazyですって。HaskellerをJuliaに誘おうって戦略だな。当然、juliaが数学屋の 道具なら、これは必須ですよ。
サンプルコードを見ていて、juliaにもtakeとかdropなんてのが有る事を知る。何処に置いて あるかと思ったら、iterator.jlだった。
# Take -- iterate through the first n elements immutable Take{I} xs::I n::Int end take(xs, n::Int) = Take(xs, n) eltype{I}(::Type{Take{I}}) = eltype(I) start(it::Take) = (it.n, start(it.xs)) function next(it::Take, state) n, xs_state = state v, xs_state = next(it.xs, xs_state) return v, (n - 1, xs_state) end function done(it::Take, state) n, xs_state = state return n <= 0 || done(it.xs, xs_state) end
startとnextとdoneを実装するとTakeを実現出来るとな。面白い、実に面白い! こういう 考え方も有ったのか。
そう言えば、以前にHaskellで血圧を月毎に集計するコードを書いた。あれも、今にして 振り返れば、start,next,doneから出来上がっていたな。まあ、あの時はそういう意識は 無かったけど、知らず知らずのうちに、そう実現してた。関数脳に冒されてしまって、 もう修復困難なのだろうな。
Debug
debuggerもPkgに有る。どうやって実現してる? 興味は尽きない。
[sakae@fedora examples]$ julia test.jl Commands: -------- h: display this help text s: step into n: step over any enclosed scope o: step out from the current scope c: continue to next breakpoint l [n]: list n source lines above and below current line (default n = 3) p cmd: print cmd evaluated in current scope q: quit debug session (calls error("interrupted")) To e.g. evaluate the variable named `n`, enter it as ` n` (with a space). Debug variables: --------------- $n: current node $s: current scope $bp: Set{Node} of enabled breakpoints $nobp: Set{Node} of disabled @bp breakpoints $pre: Dict{Node} of grafts Example usage: ------------- $(push!(bp, n)) # set breakpoint at the current node $(delete!(bp, n)) # unset breakpoint at the current node $(push!(nobp, n)) # ignore @bp breakpoint at the current node $(pre[n] = :(x = 0)) # execute x=0 just before the current node, at each visit Type an expression to evaluate it in the current scope. at /home/sakae/.julia/v0.4/Debug/examples/test.jl:12 11 x, y = 0, 1 --> 12 @bp 13 go_on = true debug:12>
macro
(隠れ)Lisperとしては、macroは基本素養。julia流を身に付けておきましょ。
macro time(ex) quote local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) elapsedtime = time_ns() - elapsedtime local diff = GC_Diff(gc_num(), stats) time_print(elapsedtime, diff.allocd, diff.total_time, gc_alloc_count(diff)) val end end
etags *.jl
上で作ったemacsメソッドを使って、ソースの海を泳ぎ出すと、引っかからないfunctionが 出てくる。例えば、
function whos(io::IO=STDOUT, m::Module=current_module(), pattern::Regex=r"") maxline = tty_size()[2] line = zeros(UInt8, maxline) :
の、tty_size()とかだ。これはもうetagsの出番? でも、juliaなんて言う新興の言語は 対応していない。manすると、--regexで、指定出来るよなんて事が書いてある。 随分前から脳力が衰えていて、正規表現はすっかり嫌いになってる。
折角juliaに巡り遭ったのだから、juliaを使ってetagsと同じ事をさせるって手を思い付いた。 どんなフォーマットになってるの? Ctags と言いつつ、etagsのフォーマットも解説されてた。
どうやら、正規表現は避けて通れそうにない。残念、頑張って脳力を付けるんじゃ、と天の 声が聞こえてきた。
諦めきれないオイラーは、もう一度etagsがサポートしてる対象言語を眺めた。そして、はたと 気が付いた。luaとかJSの関数指定って、functionじゃなかったっけ?(うろ覚えもはなはだしい!)
やってみれ。ひょっとしたら、関数だけは拾ってくれるのではなかろうか。正解でした。 ちゃんと、上のtty_sizeを見つけて、飛んで行ってくれたよ。
ちなみに
sakae@uB:/usr/share/julia/base$ grep function *.jl */*.jl | wc -l 4493 sakae@uB:/usr/share/julia/base$ wc -l TAGS 4442 TAGS
大体、網羅してるから良しとしよう。ああ、一応証拠の提示。
sakae@uB:/usr/share/julia/base$ grep tty_size -B 15 TAGS function membershiptest(15,389 function Base.Base78,2508 function Base.Base88,3132 env.jl,303 function _jl_win_getenv(12,512 function _setenv(45,1458 function _unsetenv(61,2008 function delete!delete84,2843 function next(100,3392 function done(116,3858 function next(123,4078 function length(139,4585 function show(147,4684 function withenv{withenv154,4835 function tty_size(171,5317
ふと、ソースのtar玉の中にあるcontrib内のctagsを覗いたら、恐ろしい事が 書いてあったぞ。
--langdef=julia --langmap=julia:.jl --regex-julia=/^[ \t]*(function|macro|abstract|type|typealias|immutable)[ \t]+([^ \t({[]+).*$/\2/f,function/ --regex-julia=/^[ \t]*(([^@#$ \t({[]+)|\(([^@#$ \t({[]+)\)|\((\$)\))[ \t]*(\{.*\})?[ \t]*\([^#]*\)[ \t]*=([^=].*$|$)/\2\3\4/f,function/
頭が痛くなるわい。