julia (2)
久しぶりにvirtualboxを使おうとしたら、6系を勧められた。i386なマシンに入ってるdebianを10にしたら、6系は使えないよと言われていたんで、もうWindows10で5系を使う縛りは無いのよ。
そんな訳で、6系にした(多分、枯れてきているだろうしね)。記念に、Debian10を入れたよ。 Desktopは今まで使った事が無い、Chromiumを選んでみた。主流で使う訳でもないのでお試しですだ。
それより、ホストとの連携に必要なguest additionsの方が重要。メニューから選んで追加のスクリプトを走らせるんだったな。が、エラー発生。 GuestAdditionsをゲストマシンにインストールする方法【Debian】を実行して難を逃れた。
次はWindows10に入れてるVcXsrvを使うかな。
sakae@debian:~$ export DISPLAY=localhost:10.0 sakae@debian:~$ firefox Unable to init server: Broadway display type not supported: localhost:10.0 Error: cannot open display: localhost:10.0
何か見慣れないエラーが出て来た。もしやと思って、MobaXtermを使ってみる。
┌────────────────────────────────────────────────────────────────────┐ │ • MobaXterm 10.2 • │ │ (SSH client, X-server and networking tools) │ │ │ │ ➤ SSH session to sakae@xxxx │ │ • SSH compression : ✔ │ │ • SSH-browser : ✔ │ │ • X11-forwarding : ✔ (remote display is forwarded through SSH) │ │ • DISPLAY : ✔ (automatically set on remote server) │ │ │ │ ➤ For more info, ctrl+click on help or visit our website │ └────────────────────────────────────────────────────────────────────┘ Linux debian 4.19.0-5-amd64 #1 SMP Debian 4.19.37-5+deb10u1 (2019-07-19) x86_64 sakae@debian:~$ echo $DISPLAY localhost:10.0 sakae@debian:~$ firefox
問題なくfirefoxが起動した(超スローと言う問題が有るな)。取り合えず使えるから深くは追及しない。
julia plots
julia> @time using Plots 8.461869 seconds (11.63 M allocations: 635.606 MiB, 4.03% gc time) julia> @time gr() 0.253707 seconds (99.65 k allocations: 4.926 MiB) Plots.GRBackend() julia> f(x) = sin(x) f (generic function with 1 method) julia> @time plot(f) 28.707774 seconds (52.29 M allocations: 2.552 GiB, 6.85% gc time) julia> x = 1:10; y = rand(10); julia> @time plot(x,y) 0.480239 seconds (894.22 k allocations: 45.173 MiB, 5.49% gc time) julia> x = 1:10; y = rand(10,2); ## 2line's julia> @time plot(x,y) 0.351719 seconds (501.47 k allocations: 24.753 MiB, 3.78% gc time) p1 = plot(f, color="red", label="sin(x)") p2 = plot(g, color="black", label="cos(x)") plot(p1, p2, layout=(2,1)) savefig("myplot.png")
Plots - powerful convenience for visualization in Julia
GRを使うとoctaveで出て来たカラーマップが出来る。けど、juliaの文法がちと煩雑でめんどい。
IJulia
julia> using IJulia julia> IJulia.notebook()
この方法より、素直に
(base) cent:tmp$ jupyter-notebook
こういう立ち上げ方をしてから、jupyterをエンジンに指定した方が楽だ。 ああ、勿論jupyter-notebookがインストールされてる必要が有るけどね。anacondaをインストールするのが簡便。
anacondaにしろjuliaにしろgolangにしろoctaveにしろ、ディストリビューションが提供するものを使うって言う風潮はさっぱり無くなってしまったね。アプリのインストールに関しては、Windows風と言うかスマホ流になってきてるね。まあ、その方が供給する側も楽だからか。箱庭政策とか鎖国政策とでも言えばいいのかな。
profile
所でPlotsを使おうとすると膨大な時間待たされる。この間juliaは何やってるの? 予想では、コンパイルの最終フェーズとローディングと思った。確認しよう。結果は、repl上に出るんで、なんとかそんなのを避けたい。スクリプトにして、ログを取っちゃえ。
debian:work$ cat hoge.jl using Profile Profile.init(n = 10^8, delay = 0.01) @profile using Plots Profile.print()
こんなのを、走らせる。
debian:work$ julia hoge.jl >LOG ┌ Warning: The profile data buffer is full; profiling probably terminated │ before your program finished. To profile for longer runs, call │ `Profile.init()` with a larger buffer and/or larger delay. └ @ Profile /buildworker/worker/package_linux32/build/usr/share/julia/stdlib/v1.1/Profile/src/Profile.jl:312
文句を言われた。しょうがないので、delay値を0.02にした。delayって言うよりサンプリングレートなんだよな。人を惑わす名前を付けるなよ。プンプン!
431 ./client.jl:436; _start() 431 ./client.jl:267; exec_options(::Base.JLOptions) 431 ./sysimg.jl:29; include(::Module, ::String) 431 ./loading.jl:1038; include_relative(::Module, ::Str... 431 ./boot.jl:326; include 431 ./loading.jl:853; require(::Module, ::Symbol) 431 ./loading.jl:858; require(::Base.PkgId) 2 ./loading.jl:927; _require(::Base.PkgId) 2 ./loading.jl:245; locate_package(::Base.PkgId) 1 ./loading.jl:322; manifest_uuid_path(::String, :... 1 ./loading.jl:369; project_file_manifest_path 1 ./iostream.jl:373; open 1 ./loading.jl:324; manifest_uuid_path(::String, :... 1 ./loading.jl:520; explicit_manifest_uuid_path 1 ./iostream.jl:373; open 429 ./loading.jl:937; _require(::Base.PkgId) 7 ./loading.jl:693; _require_search_from_serialize... : 3 ./compiler/typeinfer.jl:613; typeinf_ext(::Core.MethodInstance,... 3 ./compiler/typeinfer.jl:576; typeinf_ext(::Core.MethodInstance, ... 3 ./compiler/typeinfer.jl:14; typeinf(::Core.Compiler.InferenceSt...
何やら、コンパイラーとかローダー(loading)が走っているみたい。上記は、tree表示だったけど、フラット表示もやってみる。
(base) cent:tmp$ sort -nr LOGfl 669 ./compiler/typeinfer.jl 14 typeinf(::Core.Compiler.InferenceState) 669 ...stractinterpretation.jl 1191 typeinf_nocycle(::Core.Compiler.Infe... 652 ...stractinterpretation.jl 805 abstract_eval_call(::Array{Any,1}, :... 650 ...stractinterpretation.jl 890 abstract_eval(::Any, ::Array{Any,1},... 646 ...stractinterpretation.jl 776 abstract_call(::Any, ::Array{Any,1},... 611 ./loading.jl 702 _require_search_from_serialized(::Ba... 602 ./loading.jl 648 _tryrequire_from_serialized(::Base.P... 594 ...stractinterpretation.jl 1135 typeinf_local(::Core.Compiler.Infere... 452 ...stractinterpretation.jl 345 abstract_call_method(::Method, ::Any... 452 ...stractinterpretation.jl 85 abstract_call_gf_by_type(::Any, ::Ar... 451 ./compiler/typeinfer.jl 497 typeinf_edge(::Method, ::Any, ::Core... 364 ./sysimg.jl 29 include(::Module, ::String) 364 ./loading.jl 1038 include_relative(::Module, ::String) 364 ./loading.jl 937 _require(::Base.PkgId) 364 ./loading.jl 858 require(::Base.PkgId) 364 ./loading.jl 853 require(::Module, ::Symbol) 364 ./client.jl 436 _start()
tree表示よりflat表示の方が、要約されたおかげで、ログの行数が圧縮され見易い。
シリアライザーと構造体のインタープリターが煩雑に呼び出されているって事は、中間言語になってた奴を、最終ターゲットマシン用に調整(コンパイル)してるって事なのかな?
Pkg.addとかした時、プレコンパイルしてますって言ってくるけど、それはコンパイルの途中までやりましたって報告なんだろうね。と、都合のよい解釈。
本当か? ソース嫁。
edit
ソース読むにはまあ、editorが必要。そしてjuliaの中から、editorを起動出来るようになってる。試してみる。
julia> edit("test.jl") julia> emacs: standard input is not a tty julia> ^C julia>
駄目じゃん。文句を言われて起動せんかった。こういうのは、ちゃんと環境変数を整えてあげましょう、かな?
(base) cent:tmp$ echo $EDITOR (base) cent:tmp$ export EDITOR='emacs -nw' (base) cent:tmp$ julia -q julia> edit("test.jl")
やったね。これで、Ready to read source code !
julia src
懐かしいな、オイラーも昔FreeBSDでやった事がある。とっても苦労した覚えがある。もう自前でコンパイルして見るなんて言う元気は無いぞ。こう書くと後ろ向きと思われるので、前向きな弁明。この糞暑い時、余計な電力消費は避けましょう。オイラーは地球に優しい人なのさ。
なお、どうしてもコンパイルしたい人は、2Gのメモリーと5GのDisk空き容量と、4コア以上のマシンが必要ですよ。
暇に任せて、ソースを見て桶。フェムトリスプなんて言うやつも同梱されてるからね。
コンパイラーが内蔵されてる。これもそれもjitの為です。rubyは実験的にjitが導入されてるようだけど、諸般の事情でgccだけが使えるみたい、ですよね > _ko1
julia> versioninfo() Julia Version 1.1.1 Commit 55e36cc308 (2019-05-16 04:10 UTC) Platform Info: OS: Linux (x86_64-pc-linux-gnu) CPU: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz WORD_SIZE: 64 LIBM: libopenlibm LLVM: libLLVM-6.0.1 (ORCJIT, skylake)
libmとllvmが肝だからってんで、わざわざverを表記してる。でもlibLLVM-6.0.1って古くないですかい? 我OpenBSD 6.5では、llvm 7.0.1 がすでに使われてますよ。
Debugger
前回見つけておいたDebuggerを試してみる。お題は、Dictのキーにシンボルを使った時、どんな振る舞いをするかだ。キーの登録ってhashしょって当たりを付けておく。
julia> using Debugger julia> breakpoint(hash) hash julia> @run Dict(:one => 1, :two => 2) Hit breakpoint: In hash(x) at hashing.jl:18 >18 hash(x::Any) = hash(x, zero(UInt)) About to run: (zero)(UInt64) 1|debug> o
ビンゴでした。そしてoコマンドを使ってソースをeditorで閲覧。 hashing.jl
hash(x::Any) = hash(x, zero(UInt)) hash(w::WeakRef, h::UInt) = hash(w.value, h) ## hashing general objects ## hash(@nospecialize(x), h::UInt) = hash_uint(3h - objectid(x)) ## core data hashing functions ## function hash_64_64(n::UInt64) :
hashにも色々なやつが居る事を確認。何やらシンボルの扱いが一番負担が少なさそうです。って、そりゃそうだ。根源の名前登録ですから、既に済んでいるはず。
1|debug> bt [1] hash(x) at hashing.jl:18 | x::Symbol = :one [2] hashindex(key, sz) at dict.jl:169 | key::Symbol = :one | sz::Int64 = 16 : [6] Type(ps) at dict.jl:124 | ps::Tuple{Pair{Symbol,Int64},Pair{Symbol,Int64}} = (:one => 1, :two => 2) | K::DataType = Symbol | V::DataType = Int64
次は、呼び出しの履歴。たかがDictと言えども、6階層の深さがありますとな。(いや、もっと深いかも知れんけど)
1|debug> down In hashindex(key, sz) at dict.jl:169 >169 hashindex(key, sz) = (((hash(key)%Int) & (sz-1)) + 1)::Int About to run: (hash)(:one) 2|debug> down In ht_keyindex2!(h, key) at dict.jl:305 305 age0 = h.age 306 sz = length(h.keys) 307 iter = 0 308 maxprobe = h.maxprobe >309 index = hashindex(key, sz) 310 avail = 0 311 keys = h.keys 312 313 @inbounds while true
呼び出しの階層(フレーム)を辿ってみる。上に上るんだからupだと思うんだけど、何故か方法指示器が逆に設定されてる。
1|debug> so Hit breakpoint: In hash(x, h) at hashing.jl:23 >23 hash(@nospecialize(x), h::UInt) = hash_uint(3h - objectid(x)) About to run: (*)(3, 0x0000000000000000) 1|debug> so In hash(x) at hashing.jl:18 >18 hash(x::Any) = hash(x, zero(UInt)) About to run: return 0x423477b3917c935e : 1|debug> Dict{Symbol,Int64} with 2 entries: :two => 2 :one => 1
soってコマンドは、step outの略らしい。今居る階層を最後まで実行して階層を遡る。
1|debug> In hashindex(key, sz) at dict.jl:169 >169 hashindex(key, sz) = (((hash(key)%Int) & (sz-1)) + 1)::Int About to run: (rem)(0x423477b3917c935e, Int64) 1|debug> L In hashindex(key, sz) at dict.jl:169 1 1 ─ %1 = (hash)(key) >2 │ %2 = (rem)(%1, Int64) 3 │ %3 = (-)(sz, 1) 4 │ %4 = (&)(%2, %3) 5 │ %5 = (+)(%4, 1) 6 │ %6 = (typeassert)(%5, Int64) About to run: (rem)(0x423477b3917c935e, Int64)
debugプロンプトの所でLって叩くと、むき出しのコードを眺められる。飽きたらまたLする。
1|debug> so In Type(kv) at dict.jl:102 101 function Dict{K,V}(kv) where V where K 102 h = Dict{K,V}() 103 for (k,v) in kv >104 h[k] = v 105 end 106 return h 107 end About to run: (iterate)((:one => 1, :two => 2), 2) 1|debug> fr [1] Type(kv) at dict.jl:102 | kv::Tuple{Pair{Symbol,Int64},Pair{Symbol,Int64}} = (:one => 1, :two => 2) | h::Dict{Symbol,Int64} = Dict(:one=>1) | k::Symbol = :one | v::Int64 = 1 | K::DataType = Symbol | V::DataType = Int64
frコマンドで現在のフレーム内の変数値を確認出来る。これ、なかなか便利だな。
julia> break_on(:error) julia> f() = hoge[2] f (generic function with 1 method) julia> @run f() Breaking for error: ERROR: UndefVarError: hoge not defined Stacktrace: [1] f() at REPL[6]:1 In f() at REPL[6]:1 >1 f() = hoge[2] About to run: (getindex)(Main.hoge, 2) 1|debug> bt [1] f() at REPL[6]:1
これは最後の砦になるかな。
llvm
llvmの話が上で出て来たので、オイラーも試してみる。お題はdiv。まずはhelpで下調べ。 算数の割り算記号も使えるのね。それからwhichで、どこで定義されてるか、そこはかとなく確認。
help?> div search: div divrem DivideError splitdrive code_native @code_native div(x, y) ÷(x, y) The quotient from Euclidean division. Computes x/y, truncated to an integer. Examples ≡≡≡≡≡≡≡≡≡≡ julia> 9 ÷ 4 2 julia> which(div,(Int,Int)) div(x::T, y::T) where T<:Union{Int16, Int32, Int64, Int8} in Base at int.jl:232 julia> code_llvm(div,(Int64,Int64)) ; @ int.jl:232 within `div' define i64 @julia_div_9273(i64, i64) { top: %2 = icmp ne i64 %0, -9223372036854775808 %3 = icmp ne i64 %1, -1 %4 = or i1 %2, %3 %5 = icmp ne i64 %1, 0 %6 = and i1 %5, %4 br i1 %6, label %pass, label %fail fail: ; preds = %top call void @jl_throw(%jl_value_t addrspace(12)* addrspacecast (%jl_value_t* inttoptr (i64 139717233863600 to %jl_value_t*) to %jl_value_t addrspace(12)*)) unreachable pass: ; preds = %top %7 = sdiv i64 %0, %1 ret i64 %7 }
llvm用の仮想CPUの中間コードを確認。ZERO割のチェックが当然ながら入ってます。
julia> code_typed(div,(Int,Int)) 1-element Array{Any,1}: CodeInfo( 1 ─ %1 = (Base.checked_sdiv_int)(x, y)::Int64 └── return %1 ) => Int64 julia> code_native(div,(Int,Int)) .text ; ┌ @ int.jl:232 within `div' pushq %rax movabsq $-9223372036854775808, %rax # imm = 0x8000000000000000 xorq %rdi, %rax : idivq %rsi ; │ @ int.jl:232 within `div' popq %rcx retq L57: xorl %edx, %edx movl %edi, %eax divl %esi ; │ @ int.jl:232 within `div' popq %rcx retq L65: movq 8622792(%rip), %rax movq (%rax), %rdi callq 0xffffffffff5ed405 ; └
amd64用なネイティブコード。目が腐るな。
julia> div(3,0) ERROR: DivideError: integer division error Stacktrace: [1] div(::Int64, ::Int64) at ./int.jl:232 [2] top-level scope at none:0 julia> edit("int.jl")
ソースは容易に見れど、波高し。
big
少し、お手軽なやつ。
julia> pi π = 3.1415926535897... julia> big(pi) 3.141592653589793238462643383279502884197169399375105820974944592307816406286198 julia> exp(big(1)) 2.718281828459045235360287471352662497757247093699959574966967627724076630353555 julia> sqrt(big(2)) 1.414213562373095048801688724209698078569671875376948073176679737990732478462102
騒ぎを起こせ
先ほどwhichなんてのを覚えたので、foldlに適用しようと思った。
help?> foldl search: foldl mapfoldl foldr mapfoldr foldl(op, itr; [init]) julia> which(foldl, (op, itr)) ERROR: UndefVarError: op not defined Stacktrace: [1] top-level scope at none:0 julia> foldl(+) ERROR: MethodError: no method matching foldl(::typeof(+)) Closest candidates are: foldl(::Any, ::Any; kw...) at reduce.jl:90 Stacktrace: [1] top-level scope at none:0 julia> edit("reduce.jl")
腕が悪くて、扱いこなせない。そういう時は、わざとエラーを起こせば、型や定義場所を教えてもらえるのね。先ほどのZERO割で、ふとこういう技を編み出したのさ。