-
Julia-eräajojen suorittaminen CSC:n klustereilla
Julia-eräajojen suorittaminen CSC:n klustereilla
Tämä opas sisältää esimerkkejä erilaisten Julia-eräajojen suorittamisesta Puhdin, Mahdin ja LUMIn klustereilla.
Esimerkit
Nämä esimerkit havainnollistavat Julia-ympäristön käyttöä erilaisissa eräajoissa.
Ne on mukautettu yleisistä ohjeista, jotka koskevat ajotöiden suorittamista Puhdissa ja Mahdissa sekä LUMIssa.
Huomaa, että emme käytä srun-komentoa prosessien käynnistämiseen eräajokomentosarjassa.
Sen sijaan käytämme Juliaa prosessien hallintaan tai kutsumme srun-komentoa Julia-koodin sisältä.
Ennen esimerkkien suorittamista meidän täytyy instansioida Julia-projekti kirjautumissolmulla.
Suorita siis seuraava komento siinä hakemistossa, jossa Julia-ympäristösi sijaitsee ja jossa Project.toml-tiedosto on.
Voit käyttää useita säikeitä, esimerkiksi --threads=10, mikä nopeuttaa esikäännöstä.
Sarjaohjelma
Käytämme seuraavaa hakemistorakennetta ja oletamme, että se on työhakemistomme.
.
├── Project.toml # Julia-ympäristö
├── batch.sh # Slurm-eräajokomentosarja
└── script.jl # Julia-komentosarja
Esimerkki script.jl-koodista.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Monisäikeistys yhdellä solmulla
Käytämme seuraavaa hakemistorakennetta ja oletamme, että se on työhakemistomme.
.
├── Project.toml # Julia-ympäristö
├── batch.sh # Slurm-eräajokomentosarja
└── script.jl # Julia-komentosarja
Esimerkki script.jl-koodista.
# Number of threads
n = Threads.nthreads()
println(n)
# Lets fill the id of each thread to the ids array.
ids = zeros(Int, n)
Threads.@threads for i in eachindex(ids)
ids[i] = Threads.threadid()
end
println(ids)
# Alternatively, we can use the @spawn macro to run task on threads.
ids = zeros(Int, n)
@sync for i in eachindex(ids)
Threads.@spawn ids[i] = Threads.threadid()
end
println(ids)
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Moniprosessointi yhdellä solmulla
Käytämme seuraavaa hakemistorakennetta ja oletamme, että se on työhakemistomme.
.
├── Project.toml # Julia-ympäristö
├── batch.sh # Slurm-eräajokomentosarja
└── script.jl # Julia-komentosarja
Esimerkki Project.toml-projektitiedostosta.
Esimerkki script.jl-koodista.
using Distributed
# We set one worker process per core.
proc_num = Sys.CPU_THREADS
# Environment variables that we pass to the worker processes.
# We set the thread count to one since each process uses one core.
proc_env = [
"JULIA_NUM_THREADS"=>"1",
"JULIA_CPU_THREADS"=>"1",
"OPENBLAS_NUM_THREADS"=>"1",
]
# We add worker processes to the local node using LocalManager.
addprocs(proc_num; env=proc_env, exeflags="--project=.")
# We use the `@everywhere` macro to include the task function in the worker processes.
# We must call `@everywhere` after adding worker processes; otherwise the code won't be included in the new processes.
@everywhere function task()
return (worker=myid(), hostname=gethostname(), pid=getpid())
end
# We run the task function in each worker process.
futures = [@spawnat worker task() for worker in workers()]
# Then, we fetch the output from the processes.
outputs = fetch.(futures)
# Remove processes after we are done.
rmprocs.(workers())
# Print the outputs of master and worker processes.
println(task())
println.(outputs)
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Moniprosessointi usealla solmulla
Käytämme seuraavaa hakemistorakennetta ja oletamme, että se on työhakemistomme.
.
├── Project.toml # Julia-ympäristö
├── batch.sh # Slurm-eräajokomentosarja
└── script.jl # Julia-komentosarja
Esimerkki Project.toml-projektitiedostosta.
[deps]
SlurmClusterManager = "c82cd089-7bf7-41d7-976b-6b5d413cbe0a"
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
Esimerkki script.jl-koodista.
using Distributed
using SlurmClusterManager
# We set one worker process per core.
proc_num = parse(Int, ENV["SLURM_NTASKS"])
# Environment variables that we pass to the worker processes.
# We set the thread count to one since each process uses one core.
n = Threads.nthreads()
proc_env = [
"JULIA_NUM_THREADS"=>"$n",
"JULIA_CPU_THREADS"=>"$n",
"OPENBLAS_NUM_THREADS"=>"$n",
]
# We add worker processes to the local node using SlurmManager
addprocs(SlurmManager())
# We use the `@everywhere` macro to include the task function in the worker processes.
# We must call `@everywhere` after adding worker processes; otherwise the code won't be included in the new processes.
@everywhere function task()
return (worker=myid(), hostname=gethostname(), pid=getpid())
end
# We run the task function in each worker process.
futures = [@spawnat worker task() for worker in workers()]
# Then, we fetch the output from the processes.
outputs = fetch.(futures)
# Remove processes after we are done.
rmprocs.(workers())
# Print the outputs of master and worker processes.
println(task())
println.(outputs)
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
MPI-ohjelma
Käynnistämme MPI-ohjelman Julian mpiexec-käärefunktiolla.
Käärefunktio korvaa paikallisista asetuksista oikean komennon mpirun-muuttujaan MPI-ohjelman suorittamista varten.
Komento on srun Puhdissa, Mahdissa ja LUMIssa.
Kääre mahdollistaa joustavamman koodin kirjoittamisen, esimerkiksi MPI- ja ei-MPI-koodin yhdistämisen, sekä siirrettävämmän koodin, koska MPI-ohjelmien suorituskomento voi vaihdella eri alustoilla.
Huomaa, että laajamittaisissa Julia MPI -ajoissa, joissa on tuhansia rankeja, meidän täytyy jakaa depot-hakemisto paikalliseen solmutallennukseen tai muistiin ja muokata depot-polkuja vastaavasti.
Muuten pakettien lataamisesta tulee erittäin hidasta.
Käytämme seuraavaa hakemistorakennetta ja oletamme, että se on työhakemistomme.
.
├── Project.toml # Julia-ympäristö
├── batch.sh # Slurm-eräajokomentosarja
├── prog.jl # Julia MPI -ohjelma
└── script.jl # Julia-komentosarja
Esimerkki Project.toml-projektitiedostosta.
Esimerkki script.jl-koodista.
Esimerkki prog.jl Julia MPI -koodista.
using MPI
MPI.Init()
comm = MPI.COMM_WORLD
rank = MPI.Comm_rank(comm)
size = MPI.Comm_size(comm)
println("Hello from rank $(rank) out of $(size) from host $(gethostname()) and process $(getpid()).")
MPI.Barrier(comm)
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki batch.sh-eräajokomentosarjasta.
Yksi GPU
Käytämme seuraavaa hakemistorakennetta ja oletamme, että se on työhakemistomme.
.
├── Project.toml # Julia-ympäristö
├── batch.sh # Slurm-eräajokomentosarja
└── script.jl # Julia-komentosarja
Esimerkki Project.toml-projektitiedostosta.
Esimerkki script.jl-koodista.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki Project.toml-projektitiedostosta.
Esimerkki script.jl-koodista.
Esimerkki batch.sh-eräajokomentosarjasta.
Esimerkki Project.toml-projektitiedostosta.
Esimerkki script.jl-koodista.
Esimerkki batch.sh-eräajokomentosarjasta.
#!/bin/bash
#SBATCH --account=<project>
#SBATCH --partition=small-g
#SBATCH --time=00:15:00
#SBATCH --nodes=1
#SBATCH --gpus-per-node=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=1750
module use /appl/local/csc/modulefiles
module load julia
module load julia-amdgpu
julia --project=. script.jl
Useita GPU:ita MPI:n kanssa
Käytämme seuraavaa hakemistorakennetta ja oletamme, että se on työhakemistomme.
.
├── Project.toml # Julia-ympäristö
├── batch.sh # Slurm-eräajokomentosarja
├── prog.jl # Julia GPU-aware MPI -ohjelma
└── script.jl # Julia-komentosarja
Esimerkki script.jl-koodista.
Esimerkki Project.toml-projektitiedostosta.
Esimerkki prog.jl-koodista. (lähde)
using MPI
using AMDGPU
MPI.Init()
comm = MPI.COMM_WORLD
rank = MPI.Comm_rank(comm)
# select device
comm_l = MPI.Comm_split_type(comm, MPI.COMM_TYPE_SHARED, rank)
rank_l = MPI.Comm_rank(comm_l)
device = AMDGPU.device_id!(rank_l+1)
gpu_id = AMDGPU.device_id(AMDGPU.device())
# select device
size = MPI.Comm_size(comm)
dst = mod(rank+1, size)
src = mod(rank-1, size)
println("rank=$rank rank_loc=$rank_l (gpu_id=$gpu_id - $device), size=$size, dst=$dst, src=$src")
N = 4
send_mesg = ROCArray{Float64}(undef, N)
recv_mesg = ROCArray{Float64}(undef, N)
AMDGPU.synchronize()
rank==0 && println("start sending...")
MPI.Sendrecv!(send_mesg, dst, 0, recv_mesg, src, 0, comm)
println("recv_mesg on proc $rank: $recv_mesg")
rank==0 && println("done.")
Esimerkki batch.sh-eräajokomentosarjasta.
#!/bin/bash
#SBATCH --account=<project>
#SBATCH --partition=small-g
#SBATCH --time=00:15:00
#SBATCH --nodes=2
#SBATCH --gpus-per-node=8
#SBATCH --ntasks-per-node=8
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=0
module use /appl/local/csc/modulefiles
module load julia
module load julia-mpi
module load julia-amdgpu
julia --project=. script.jl
Huomioita
Monisäikeistys lineaarialgebrassa
Julia käyttää oletusarvoisesti OpenBLASia LinearAlgebra-taustajärjestelmänä.
Ulkoiset lineaarialgebran taustajärjestelmät, kuten OpenBLAS, käyttävät sisäistä säikeistystä.
Voimme asettaa niiden säiemäärät ympäristömuuttujilla.
julia-moduuli asettaa ne CPU-säikeiden määrään.
Meidän täytyy varoa ylivaraamasta ytimiä, kun käytämme BLAS-operaatioita Julia-säikeiden tai -prosessien sisällä.
Voimme muuttaa BLAS-säikeiden määrää ajon aikana BLAS.set_num_threads-funktiolla.
using LinearAlgebra
# Number of threads
n = Threads.nthreads()
# Define a matrix
X = rand(1000, 1000)
# Set the number of threads to one before performing BLAS operations of multiple Julia threads.
BLAS.set_num_threads(1)
Y = zeros(n)
Threads.@threads for i in 1:n # uses n Julia threads
Y[i] = sum(X * X) # uses one BLAS thread
end
# Set the number of threads back to the default when performing BLAS operation on a single Julia Thread.
BLAS.set_num_threads(n)
Z = zeros(n)
for i in 1:n # uses one Julia thread
Z[i] = sum(X * X) # uses n BLAS threads
end
Vaihtoehtoisesti voimme käyttää MKL-taustajärjestelmää MKL.jl:n kautta lineaarialgebran taustajärjestelmänä. MKL on usein OpenBLASia nopeampi käytettäessä useita säikeitä Intelin CPU:illa, kuten Puhdissa. Voimme asettaa MKL:n säiemäärän seuraavasti.
Jos käytämme MKL:ää, se tulisi ladata ennen muita lineaarialgebrakirjastoja.
Eri BLAS-säiemäärien kuin yhden tai kaikkien ytimien käyttämisessä OpenBLASin ja MKL:n kanssa on huomioitavia rajoitteita.