Python/Julia memo
Julia
ShellModel.jl https://github.com/SotaYoshida/ShellModel.jlの開発で得たTips
基本的に速度が出ないときは
公式のPeformance Tipsを参照する。
(当たり前っちゃ当たり前だが)不必要なメモリの割当を避ける
たとえばループの中で行列のコレスキー分解の(陽な)計算が必要な場合
#A: m×m given matrix
for i =1:10^6
tL = cholesky(A).L
end
などとすると、10^6回メモリの割当が発生してしまう。
#A: m×m given matrix, L: LowerTriangular matrix
#my_cholesky!: destructive version of Cholesky decomposition
tL = LowerTriangular(zeros(Float64,m,m))
for i =1:10^6
my_cholesky!(A,tL)
end
などとして、pre-allocatedな行列を更新する"destructive"な関数を作り使用すると大幅に速度が改善する。
同様にして、ブロードキャストを用いることで不要なメモリの割当を避ける
BLASの関数への置き換えを検討する
3つのInt64を深い階層のArrayにして持って使うよりも、3つのInt64自体を一つのstructに押し込んで使うほうが速い事が多かった。
(理由はあんまりよく分かってない.boundary checkとか?)関数を定義するときに
function f(ln::::Array{Array{Int64,1}},
occs::FA) where {FA<:Array{Float64,1}}
...
end
と型のアノテーションを付与できるが「パフォーマンスの改善に必要」というのは、やや不正確かも。
(型の不確実性を避けることで速くなる、という意味ではあってる)
コードの開発段階で、意図したものと違うAny型の配列などが紛れ込んでパフォーマンスが低下するのを防ぐのに、アノテーションを書くと良い。
抽象型に依る速度低下のサンプルコード
function f1(V1)
for i=1:10^5
for j=1:10^3
for tmp in V1[i][j]
tmp*0.001
end
end
end
end
function f2(V2)
for i = 1:10^5
for j=1:10^3
for tmp in V2[i][j]
tmp*0.001
end
end
end
end
function main()
V1 = [ [ [] for j=1:10^3 ] for i=1:10^5]
for i=1:10^5
for j =1:10^3
push!(V1[i][j],i*j*1.e-2)
end
end
V2 = [ [ Float64[] for j=10^3 ] for i=1:10^5]
for i=1:10^5
for j =1:10^3
push!(V2[i][j],i*j*1.e-2)
end
end
print("V1 \t")
@time f1(V1)
print("V2 \t")
@time f2(V2)
print("V1 \t")
@time f1(V1)
print("V2 \t")
@time f2(V2)
end
main()
入れ子のリストに対して何らかの操作を行う事を考えてみる。
上のコードを実行すると、f2に比べてf1は2桁くらい遅い(factorは環境に依る)。
f1では、演算tmp*0.01部分でメモリアロケーションが発生するのが主な原因。
また、tmp*0.01をコメントアウトして、ループさせるだけだとメモリアロケーションは起こらないが、やはりf1は1桁遅かった。
Julia v1.5.3とv1.6.0rc-1で、パフォーマンスが異なるコードを発見した。
vs = [ zeros(Float64,dim) for i=1:num]のような1次元配列のArray (Array{Array{Float64,1}})にループ内でアクセス
v1 = vs[i]; v2=vs[i+1]してdot積を取るような場合、v1.5.3ではv1,v2の型はArray{Float64,1}なのに対し、
v1.6.0 rc-1ではVector{Float64}で、後者のほうがdot積が桁で速くなっていた。
Julia PERLでa = [ zeros(Float64,10^6) for i=1:10]とやっても確認できる。
Vector{Float64}は単にArray{Float64}のaliasっぽいけど...??
Python
M1 Macでのmatplotlib環境を構築したときのメモ書き
(自分用のメモなので、実行して不具合があっても責任は取れません)
を参考にすると、今まで使っていたお絵かきスクリプトがpython3 hogehoge.pyで動くようになった。
Julia側からPyCallでmatplotlibを使う場合、普段使いのPythonとJuliaで用いるPythonのversionが異なる。
M1 MacでのPython環境構築は現状手探りなところもあるので、分けて使うのが当面良さそう。
実際、普段使いのpython3にENV["PYTHON"]で用いるPythonを指定してPyCallをbuildすると、PyCallを呼ぼうとするとJuliaが強制終了するようになってしまった。
(一旦、Juliaのバイナリとホームディレクトリにある~/.juliaを消す羽目になった)
再度バイナリをインストールしてライブラリを再インストール → using PyCall でJulia内のpythonが読み込まれるところまで復旧
JuliaはCondaを持っているので、そこで必要なライブラリを整備することにする。まずconda initする
$~/.julia/conda/3/bin/conda init bash
(当然、最後の部分は使っているシェルに依存)
$conda activate
でconda環境に入り、
(base) ~ $ conda install matplotlib
でmatplotlibを導入
JuliaでPyCallをrm→再インストールで、
julia>using PyCall
julia>@pyimport matplotlib
が出来るようになった。
これをやったのは2021年の3月くらいで、その後変わってそう。