や、言語関係など覚えたいもの、覚えるべきものはたくさんある一方で、注目が集まっているから、やってみたい。ということでプログラミング言語としてのを学んでいく。
4. Juliaの高速化
4.2 最適化しやすいコード
の処理系では、コンパイラは動的にコンパイルするため、何気ないコードが想定外のパフォーマンス低下をもたらす可能性がある。
4.2.4 グローバル変数への参照
グローバル変数を不用意に用いると、型の不確実性が生じることがある。グローバル変数がどこからでも参照可能で、定数でない限り実行中にいつでもオブジェクトを変更できるからである。
可能であれば関数内に収めるなどで型を決定させるのが良いが、状況によっては性能が必要なコードでグローバル変数を参照したい場合がある。その場合、ループの内側などで頻繁に参照されるグローバル変数には、可能であればキーワードをつけて定数化することが推奨される。
########################################### ### Constキーワードを用いた場合の型推論 ### ########################################### using Profile # (1) Constなしの場合 G1 = 6.674 * 10^-11 # 万有引力定数 # (2) Constありの場合 const G2 = 6.674 * 10^-11 # 万有引力定数 # 2質点間の万有引力 force1(m1, m2 , r) = G1 * m1 * m2 / r^2 force2(m1, m2 , r) = G2 * m1 * m2 / r^2 println(@code_warntype force1(1.0, 2.0, 3.2)) println(" ") println("***********") println(" ") println(@code_warntype force2(1.0, 2.0, 3.2))
またグローバル変数の型は変化させず、値を変更させたい場合もある。このときを用いることも考えられる。
4.2.5 コレクションや構造体での型不確実性
ではコレクションや構造体も不用意に定義すると型不確実性の問題が生じる。
たとえば
xs = []
と定義すると、の型はとなる。
ここから値を取得するなど(!や!)しても方は推定できないままである。そのため、方は決まっているが要素が無い配列を定義するのであれば、などと定義すればよい。
4.2.6 メモリレイアウト
計算機では、多次元配列も1次元のアドレス空間を持つメモリに保持される。そのため、多次元配列は何らかの方法で1次元に変換されて保存・参照されることになる。この変換方法に関与するのがメモリレイアウトである。
メモリレイアウトを意識することは、配列要素へのアクセスを頻繁に行うプログラムを書く上で非常に重要である。今日の計算機はメモリの読み書きが上で行う他の計算よりも非常に遅いため、より読み書きが高速になるようにメモリの一部を内にキャッシュしている。メモリの読み書きを行なう際にはアドレス上の近い領域を集中的に参照した方がキャッシュが効率的に利用される。したがって多次元配列で各要素がどのようにアドレス空間上に配置されているのかを把握した方が良い。
たとえば多次元配列を走破する場合、可能な限りメモリ上の近い要素が並ぶ方向にすべきである。簡単のため2次元配列を考えると、において型は行列の要素を- (列の各要素がアドレス空間で隣り合う)で配置している。そのため文で走破するには列方向の反復を内側に入れた方がキャッシュを有効に使えて実行速度が上がる*1。
######################## ### メモリレイアウト ### ######################## function column_first(X) m, n = size(X) for j in 1:m # 列の添字 for i in 1:n # 行の添字 end end end function row_first(X) m, n = size(X) for i in 1:n # 行の添字 for j in 1:m # 列の添字 end end end num = 10000 results1 = 0.0 results2 = 0.0 for i in 1:num X = Array{Int}(undef, 10000,10000) result1, runtime1 = @timed column_first(X) result2, runtime2 = @timed row_first(X) results1 += runtime1 results2 += runtime2 end println(results1/num) println(results2/num)