質問は「恥ずかしそうに並行」の検索タスクを行うための基本パターンのようです。 @parallel for
構成は仕切り作業には適していますが、for
として停止するための短絡ロジックは、単一プロセスフローではありません(break
)。
ジュリアでこれを行う方法を実証するには、いくつかのホイールと組み合わせたロックの組み合わせを見つけるというおもちゃの問題を考えてみましょう。ホイールの各設定は、いくつかの方法(正確にはcombodelay
の時間をとる - 下のコードを参照)で正確性をチェックすることができます。ホイールの正しい番号が見つかると、次のホイールが検索されます。高レベルの擬似コードは、OPの質問に記載されているスニペットに似ています。
以下は、これを実行するための実行コード(0.5と0.6)です。いくつかのコメントは詳細を説明し、コードは簡単な切り取りと貼り付けのために単一のチャンクで与えられます。
# combination lock problem parameters
const wheel_max = 1000 # size of wheel
@everywhere const magic_number = [55,10,993] # secret combination
const wheel_count = length(magic_number) # number of wheels
const combodelay = 0.01 # delay time to check single combination
# parallel short-circuit parameters
const check_to_work_ratio = 160 # ratio to limit short-circuit overhead
function find_combo(wheel,combo=Int[])
done = SharedArray{Int}(1) # shared variable to hold if and what combo
done[1] = 0 # succeded. 0 means not found yet
# setup counters to limit parallel overhead
@sync begin
@everywhere global localdone = false
@everywhere global checktime = 0.0
@everywhere global worktime = 0.0
end
# do the parallel work
@sync @parallel for i in 1:wheel_max
global localdone
global checktime
global worktime
# if not checking too much, look at shared variable
if !localdone && check_to_work_ratio*checktime < worktime
tic()
localdone = done[1]>0
checktime += toq()
end
# if no process found combo, check another combo
if !localdone
tic()
sleep(combodelay) # simulated work delay, {..statement..} from OP
if i==magic_number[wheel] # {condition} from OP
done[1] = i
localdone = true
end
worktime += toq()
else
break
end
end
if done[1]>0 # check if shared variable indicates combo for wheel found
push!(combo,done[1])
return wheel<wheel_count ? find_combo(wheel+1,combo) : (combo,true)
else
return (combo,false)
end
end
function find_combo_noparallel(wheel,combo=Int[])
found = false
i = 0
for i in 1:wheel_max
sleep(combodelay)
if i==magic_number[wheel]
found = true
break
end
end
if found
push!(combo,i)
return wheel<wheel_count ?
find_combo_noparallel(wheel+1,combo) : (combo,true)
else
return (combo,false)
end
end
function find_combo_nostop(wheel,combo=Int[])
done = SharedArray{Int}(1)
done[1] = 0
@sync @parallel for i in 1:wheel_max
sleep(combodelay)
if i==magic_number[wheel]
done[1] = i
end
end
if done[1]>0
push!(combo,done[1])
return wheel<wheel_count ?
find_combo_nostop(wheel+1,combo) : (combo,true)
else
return (combo,false)
end
end
result = find_combo(1)
println("parallel with short-circuit stopping: $result")
@assert result == (magic_number, true)
result = find_combo_noparallel(1)
println("single process with short-circuit stopping: $result")
@assert result == (magic_number, true)
result = find_combo_nostop(1)
println("parallel without short-circuit stopping: $result")
@assert result == (magic_number, true)
println("\ntimings")
print("parallel with short-circuit stopping ")
@time find_combo(1);
print("single process with short-circuit stopping ")
@time find_combo_noparallel(1)
print("parallel without short-circuit stopping ")
@time find_combo_nostop(1)
nothing
実装が見栄えが良く、メタプログラミングによっては、短絡機構の一部を隠すことができます。しかしこれはスタートとしては良いはずです。
結果は次のように約になります。
parallel with short-circuit stopping: ([55,10,993],true)
single process with short-circuit stopping: ([55,10,993],true)
parallel without short-circuit stopping: ([55,10,993],true)
timings
parallel with short-circuit stopping 4.473687 seconds
single process with short-circuit stopping 11.963329 seconds
parallel without short-circuit stopping 11.316780 seconds
これは3つのワーカー・プロセスとのデモンストレーションのために計算されます。実際の問題には、より多くのプロセスとプロセスごとの作業が必要であり、短絡のメリットが明らかになります。