すばらしい質問です。
このフィボナッチ関数のマルチスレッド実装は、シングルスレッドバージョンよりも高速ではありません。この関数は、新しいスレッド機能がどのように機能するかを示すおもちゃの例としてブログ投稿にのみ示され、さまざまな関数で多数のスレッドを生成でき、スケジューラーが最適なワークロードを計算することを強調しています。
問題は@spawn
、約の重要なオーバーヘッドがある1µs
ことです。そのため、スレッドを生成して未満のタスクを実行すると1µs
、パフォーマンスが低下する可能性があります。の再帰的な定義はfib(n)
、次数1.6180^n
[1]の指数関数的な時間の複雑さを持っているため、を呼び出すとfib(43)
、なんらかの順序1.6180^43
スレッドを生成します。それぞれが1µs
スポーンするのにかかる場合、必要なスレッドをスポーンしてスケジュールするだけで約16分かかります。これは、実際の計算を実行してスレッドを再マージ/同期するのにかかる時間も考慮していません。より多くの時間。
計算の各ステップでスレッドを生成するこのようなことは、計算の各ステップが@spawn
オーバーヘッドに比べて長い時間がかかる場合にのみ意味があります。
のオーバーヘッドを減らす作業が進んでいることに注意してください。ただし@spawn
、マルチコアシリコンチップの物理的性質により、上記のfib
実装には十分高速であるとは思えません。
スレッド化されたfib
関数を実際に有益なものに変更する方法に興味があるfib
場合は1µs
、実行するよりも大幅に時間がかかると考えられる場合にのみ、スレッドを生成するのが最も簡単です。私のマシン(16物理コアで実行)では、
function F(n)
if n < 2
return n
else
return F(n-1)+F(n-2)
end
end
julia> @btime F(23);
122.920 μs (0 allocations: 0 bytes)
そのため、スレッドを生成するコストよりも2桁優れています。それは使用するのに良いカットオフのようです:
function fib(n::Int)
if n < 2
return n
elseif n > 23
t = @spawn fib(n - 2)
return fib(n - 1) + fetch(t)
else
return fib(n-1) + fib(n-2)
end
end
今、私がBenchmarkTools.jl [2]で適切なベンチマーク方法論に従っている場合、私は見つけます
julia> using BenchmarkTools
julia> @btime fib(43)
971.842 ms (1496518 allocations: 33.64 MiB)
433494437
julia> @btime F(43)
1.866 s (0 allocations: 0 bytes)
433494437
@Anushはコメントで質問します。これは、16コアを使用すると2倍高速になると思われます。16倍のスピードアップに近いものを取得することは可能ですか?
はい、そうです。上記の関数の問題は、関数の本体がのそれよりも大きく、F
多くの条件、関数/スレッドの生成などがあることです。比較してみてください@code_llvm F(10)
@code_llvm fib(10)
。これは、fib
ジュリアにとって最適化がはるかに難しいことを意味します。この余分なオーバーヘッドにより、小さなn
ケースで違いが生まれます。
julia> @btime F(20);
28.844 μs (0 allocations: 0 bytes)
julia> @btime fib(20);
242.208 μs (20 allocations: 320 bytes)
大野!触れられることのない余分なコードはすべて、n < 23
私たちを一桁遅くしています!ただし、簡単な修正があります。の場合n < 23
、に再帰しないでfib
、代わりにシングルスレッドを呼び出しますF
。
function fib(n::Int)
if n > 23
t = @spawn fib(n - 2)
return fib(n - 1) + fetch(t)
else
return F(n)
end
end
julia> @btime fib(43)
138.876 ms (185594 allocations: 13.64 MiB)
433494437
これにより、非常に多くのスレッドで期待される結果により近い結果が得られます。
[1] https://www.geeksforgeeks.org/time-complexity-recursive-fibonacci-program/
[2] BenchmarkTools.jlのBenchmarkTools @btime
マクロは、コンパイル時間と平均結果をスキップして、関数を複数回実行します。