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 Tutorial

Plots - powerful convenience for visualization in Julia

Julia Package GR

GRを使うとoctaveで出て来たカラーマップが出来る。けど、juliaの文法がちと煩雑でめんどい。

IJulia

julia> using IJulia

julia> IJulia.notebook()

この方法より、素直に

(base) cent:tmp$ jupyter-notebook

こういう立ち上げ方をしてから、jupyterをエンジンに指定した方が楽だ。 ああ、勿論jupyter-notebookがインストールされてる必要が有るけどね。anacondaをインストールするのが簡便。

anacondaにしろjuliaにしろgolangにしろoctaveにしろ、ディストリビューションが提供するものを使うって言う風潮はさっぱり無くなってしまったね。アプリのインストールに関しては、Windows風と言うかスマホ流になってきてるね。まあ、その方が供給する側も楽だからか。箱庭政策とか鎖国政策とでも言えばいいのかな。

Jupyter Notebook 効果的な使い方を知ろう

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

Juliaをソースからビルドする

懐かしいな、オイラーも昔FreeBSDでやった事がある。とっても苦労した覚えがある。もう自前でコンパイルして見るなんて言う元気は無いぞ。こう書くと後ろ向きと思われるので、前向きな弁明。この糞暑い時、余計な電力消費は避けましょう。オイラーは地球に優しい人なのさ。

なお、どうしてもコンパイルしたい人は、2Gのメモリーと5GのDisk空き容量と、4コア以上のマシンが必要ですよ。

暇に任せて、ソースを見て桶。フェムトリスプなんて言うやつも同梱されてるからね。

古典コンパイラ入門

(LLVM IRとNative code 触りだけ)

コンパイラーが内蔵されてる。これもそれも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割で、ふとこういう技を編み出したのさ。

etc

Julia Advent Calendar 2018

Juialang: print 入門 - Hello World から pretty-print まで -

FreeBSD あれこれ