本日は
アドベントカレンダー $N=10$ 日目です.
複数台のラズパイを使って Distributed.jl で分散計算をさせる(その二)です.
下記の記事の続きです
前回はシンプルな和をとる計算で計算させましたが,今回はマンデルブロ集合を計算しましょう.この計算は広い領域を複数の領域に分割し,分割した領域に対してラズパイが計算を担当するというものでやや高度なものになります.この場合は Distributed.jl だけではなく配列のやり取りも含むので DistributedArrays.jl を使うことになります.
前提
- ラズパイなど複数の計算マシンを持っている
- 作業マシンに Julia が入っており,いくつかのパッケージを導入できる権限を持っていること
- 作業マシンと計算マシンの間でSSH 接続ができること
計算マシンで行うこと
- Julia を事前にインストールしておく
- 下記の三つのパッケージを入れておく
pkg> add DistributedArrays OffsetArrays ImageCore Sixel
作業マシンで行うこと.
- Julia を事前にインストールしておく
- 下記の三つのパッケージを入れておく
pkg> add DistributedArrays OffsetArrays ImageCore Sixel
- 各々の計算マシンに対して SSH 接続ができることを確認
計算スクリプトの用意
次のようなスクリプトを用意しておきます.
using Distributed
if nworkers() == 1
@info "Start machines..."
p = addprocs(
[
("terasaki@rpi5-1.local", 1),
("terasaki@rpi5-2.local", 1),
("terasaki@rpi5-3.local", 1),
("terasaki@rpi5-4.local", 1),
("terasaki@rpi5-5.local", 1),
("terasaki@rpi5-6.local", 1),
("terasaki@rpi5-7.local", 1),
("terasaki@rpi5-8.local", 1),
],
dir = "/home/terasaki",
exename = "/home/terasaki/.juliaup/bin/julia",
exeflags = "--threads=4",
)
@info "Machines launched!"
end
@info "Running @everywhere macro"
@everywhere begin
using DistributedArrays, OffsetArrays
Base.@kwdef struct Param{I,F}
xmin::F = -1.75
xmax::F = 0.75
width::I = 4096
#width::I = 256
ymin::F = -1.25
ymax::F = 1.25
height::I = 4096
#height::I = 256
max_iter::I = 500
end
function mandelbrot_kernel(param::Param, c)
(; max_iter) = param
z = c
for i = 0:max_iter-1
z = z * z + c
abs(z) > 2 && return i
end
max_iter
end
end # everywhere
function makeaxes(param::Param)
(; xmin, xmax, width, ymin, ymax, height) = param
x = range(xmin, xmax, width + 1)[begin:end-1]
y = range(ymin, ymax, height + 1)[begin:end-1]
x, y
end
function makeinput(param::Param)
x, y = makeaxes(param)
complex.(x, y')
end
function distributed_compute_mandelbrot(param::Param, input)
e = @elapsed Data = distribute(input)
@info "distribute" e
e = @elapsed Result = DArray(size(Data)) do inds # local indices on each processor
arr = zeros(inds) # We create an OffsetArray with the correct local indices
data_local = OffsetArray(localpart(Data), localindices(Data)) # get the local part of data on each processor and assign the proper indices
Base.Threads.@threads for i in eachindex(data_local)
c = data_local[i]
arr[i] = mandelbrot_kernel(param, c)
end
parent(arr) # strip the OffsetArray wrapper
end
@info "Calculation" e
return Result
end
using ImageCore
using Sixel
@info "Setup done"
@info "We have $(nworkers()) workers"
function main()
param = Param()
inputz = makeinput(param)
@time doutput = distributed_compute_mandelbrot(param, inputz)
e = @elapsed output = Matrix(doutput) # gather data in each processor and collect to host machie
@info "Gather data " e
@assert maximum(output) > 0
normalized = output / maximum(output)
gray = colorview(Gray, normalized')
sixel_encode(gray)
end
@info "Run main() manually"
if abspath(PROGRAM_FILE) == @__FILE__
main()
end
作業マシンで include("mandelbrot.jl") を実行します.
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.12.2 (2025-11-20)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org release
|__/ |
julia> include("mandelbrot.jl")
[ Info: Start machines...
[ Info: Machines launched!
[ Info: Running @everywhere macro
[ Info: Setup done
[ Info: We have 8 workers
[ Info: Run main() manually
main() を実行します.
ラズパイ側で計算が実行されていることが確認できます.領域によって計算がすぐに終わるのでCPUの負荷は担当する領域によってまちまちになります.
iTerm2 上で実行すると下記のように画像データを表示することができます.多分計算ができてるでしょう.
データの分配の 6 秒,計算に 15 秒, 集約に 5 秒かかってることになります.総計で概ね 21 秒かかっています.
手元のマシン (M2 Max 12 core) で行うと下記のようになりました.
julia> main()
┌ Info: distribute
└ e = 1.026301666
┌ Info: Calculation
└ e = 4.5218025
5.554594 seconds (2.77 M allocations: 395.196 MiB, 0.23% gc time, 11.88% compilation time)
┌ Info: Gather data
└ e = 0.476176084
前回はラズパイクラスタが勝ちましたが,今回のタスクだと手元のマシンが勝ちましたね.
まとめ
DistributedArrays.jl を使う分散計算の例を紹介しました.
あまり例がなかったのでお役に立てれば幸いです.

