-
R:n rinnakkaisten eräajojen esimerkkejä
R:n rinnakkaisten eräajojen esimerkkejä
r-env-moduulia voidaan käyttää rinnakkaislaskentaan useilla tavoilla. Näihin kuuluvat moniydin- ja taulukkotyöajot sekä MPI:hin (Message Passing Interface) perustuvat ajot, joissa käytetään useita laskentasolmuja.
Tällä sivulla annetaan esimerkkejä siitä, miten erilaisia rinnakkaisia R-eräajoja suoritetaan. Aloittaaksesi rinnakkaisen R:n käytön ja saadaksesi lisää vinkkejä, katso johdanto rinnakkaisiin R-ajoihin.
Voit myös tutustua asiaankuuluviin R-pakettien käyttöohjeisiin sekä CSC:n Geocomputing-esimerkkeihin, joissa on lisää esimerkkejä rinnakkaislaskennasta raster-paketin avulla.
Huomioita useiden ytimien varaamisesta
Oletuksena R käyttää yhtä ydintä. Useiden ytimien varaaminen sopii tilanteisiin, joissa käytät R-pakettia, joka on rakennettu hyödyntämään useita ytimiä (piilotettu rinnakkaisuus), tai R-skriptisi on kirjoitettu käyttämään useita ytimiä. Jos et ole varma, voiko R-koodisi hyödyntää useita ytimiä, voit
- tutustua R-paketin dokumentaatioon
- käyttää seff-komentoa suorittimen käytön tehokkuuden tarkistamiseen
- tehdä testiajoja eri ydinmäärillä ja verrata suoritusaikoja
- tarkistaa prosessien määrän koodin ajon aikana työkaluilla kuten
htop - pyytää neuvoja CSC:n Service Deskiltä
Pelkkä ytimien määrän lisääminen ei välttämättä takaa nopeampaa laskentaa. Mutta jos analyysisi voidaan suorittaa rinnakkain useilla ytimillä, supertietokone voi todella nopeuttaa sitä.
Taulukkotyöt
Taulukkotöitä voidaan käyttää embarrassingly parallel -tehtävien käsittelyyn ja useiden samanaikaisesti suoritettavien Slurm-töiden lähettämiseen. Alla oleva esimerkkiskripti lähettäisi työn, joka sisältää kymmenen toisistaan riippumatonta osatehtävää small-osiolla siten, että kukin niistä vaatii alle 45 minuuttia laskenta-aikaa ja alle 2 Gt muistia.
#!/bin/bash
#SBATCH --job-name=r_array
#SBATCH --account=<project>
#SBATCH --output=output_%A_%a.txt
#SBATCH --error=errors_%A_%a.txt
#SBATCH --partition=small
#SBATCH --time=00:45:00
#SBATCH --array=1-10
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=1
#SBATCH --mem-per-cpu=2000M
# Load r-env
module load r-env
# Run the R script
srun Rscript --no-save myscript.R $SLURM_ARRAY_TASK_ID
#!/bin/bash
#SBATCH --job-name=r_array
#SBATCH --account=<project>
#SBATCH --output=output_%A_%a.txt
#SBATCH --error=errors_%A_%a.txt
#SBATCH --partition=small
#SBATCH --time=00:45:00
#SBATCH --array=1-10
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=1
#SBATCH --mem-per-cpu=2000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R $SLURM_ARRAY_TASK_ID
#!/bin/bash
#SBATCH --job-name=r_array
#SBATCH --account=<project>
#SBATCH --output=output_%A_%a.txt
#SBATCH --error=errors_%A_%a.txt
#SBATCH --partition=small
#SBATCH --time=00:45:00
#SBATCH --array=1-10
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=1 # Each core gives 1.875 GB of memory
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R $SLURM_ARRAY_TASK_ID
Jos haluaisimme käyttää taulukon numeroa $SLURM_ARRAY_TASK_ID R-skriptissämme, voisimme käyttää commandArgs- tai Sys.getenv-funktiota.
Taulukon numeroa $SLURM_ARRAY_TASK_ID voitaisiin käyttää esimerkiksi määrittämään, mikä datajoukko tai parametrijoukko analysoidaan kussakin taulukossa.
Taulukkotyöt soveltuvat parhaiten tapauksiin, joissa kukin osatehtävä kestää yli noin 30 minuuttia ja osatehtävien määrä on enintään 400. Suurempiin taulukkoasetelmiin, joissa osatehtävät ovat lyhyempiä, katso alla oleva esimerkki laajamittaisista taulukkotöistä GNU parallelin avulla
Moniydintyöt
Seuraava eräajotiedosto näyttää, miten lähetetään useita ytimiä yhdellä solmulla hyödyntävä työ.
Työ varaa yhden tehtävän (--ntasks=1), kahdeksan ydintä (--cpus-per-task=8) ja yhteensä 8 Gt muistia (--mem-per-cpu=1000M). Suoritusaika on rajoitettu viiteen minuuttiin.
#!/bin/bash
#SBATCH --job-name=r_multicore
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=1000M
# Load r-env
module load r-env
# Run the R script
srun Rscript --no-save myscript.R
#!/bin/bash
#SBATCH --job-name=r_multicore
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=1000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
#!/bin/bash
#SBATCH --job-name=r_multicore
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=8 # Each core gives 1.875 GB of memory
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
future-pakettia käyttävät moniydintyöt
future-pakettiperhe tarjoaa monipuolisia tapoja rinnakkaistaa R-ajoja vähäisellä valmistelulla. future-paketti tarjoaa kehikon futureja käyttäville R-ajoille (katso lisätietoja future-paketin CRAN-sivulta). Se, ratkaistaanko futuret peräkkäin vai rinnakkain, määritetään plan()-funktiolla.
Analyyseihin, joissa käytetään useita ytimiä yhdellä solmulla, sopivat plan(multisession) ja plan(multicore). Ensimmäinen käynnistää useita toisistaan riippumattomia R-prosesseja ja jälkimmäinen haarauttaa olemassa olevan R-prosessin. Huomaa, että plan(multicore) ei toimi RStudiossa. plan(cluster) sopii töihin, joissa käytetään useita solmuja (katso alla).
Lähetettäessä työtä, joka käyttää multisession- tai multicore-futureja, tulee määrittää yksi solmu (--nodes=1), yksi tehtävä (--ntasks=1) ja ytimien määrä (--cpus-per-task=x). Oletuksena työntekijöiden määrä on parallelly::availableCores()-funktion palauttama ytimien määrä. Ohjeita eräajotiedostojen suunnitteluun löytyy yllä olevasta moniydineräajon esimerkistä.
Alla olevaa R-skriptiä voitaisiin käyttää analyysiaikojen vertaamiseen peräkkäisten, multisession- ja multicore-strategioiden välillä.
library(future)
library(tictoc)
library(furrr)
# Different future plans (choose one)
# (Note: three cores and thus three workers were used in this example)
# plan(sequential)
# plan(multisession)
# plan(multicore)
# Analysis timing
tic()
nothingness <- future_map(c(2, 2, 2), ~Sys.sleep(.x))
toc()
# sequential: 6.157 sec
# multisession: 2.463 sec
# multicore: 2.212 sec
Käytännön esimerkkejä future-ajoista, joissa käytetään plan(multicore)- ja plan(cluster)-asetuksia (kuten alla kohdassa usean solmun R-ajot MPI:n avulla) rasteridatan kanssa, löytyy CSC:n Geocomputing-esimerkeistä.
Yksi future-paketin eduista on, että globaalit muuttujat viedään automaattisesti ja ovat rinnakkaisprosessien käytettävissä. Kun rinnakkaislaskenta määritetään parallel- tai snow-paketeilla, voi olla tarpeen ladata paketit uudelleen
skriptin siinä osassa, joka suoritetaan rinnakkain, ja käyttää clusterExport-funktiota, jotta globaalissa ympäristössä olevat objektit saadaan rinnakkaisprosessien käyttöön.
Suorituskyvyn parantaminen säikeistyksellä
r-env on käännetty käyttäen Intel® oneAPI Math Kernel Librarya (oneMKL), mikä mahdollistaa data-analyysitehtävien suorittamisen useilla säikeillä. Lisätietoja säikeistyksestä löytyy Intel®-verkkosivustolta.
Oletuksena r-env on yksisäikeinen. Tietyt R-paketit, kuten data.table, mgcv ja ranger, tarjoavat suoran tuen monisäikeistykselle. Myös muita R-paketteja käyttävät työt voivat hyötyä monisäikeistyksestä analyysistä riippuen. Esimerkiksi monisäikeistys voi nopeuttaa lineaarialgebran rutiineja. Selvittääksesi, hyödyttääkö monisäikeistys tiettyä analyysiä, suosittelemme kokeilemaan eri säiemääriä ja vertailemaan koodisi suorituskykyä pienellä esimerkkiaineistolla sekä esimerkiksi R-paketilla microbenchmark.
Moduuli käyttää OpenMP-säikeistysteknologiaa, ja säikeiden määrää voidaan hallita ympäristömuuttujalla OMP_NUM_THREADS. Käytännössä säikeiden määrä asetetaan vastaamaan työssä käytettävien ytimien määrää. Huomaa, että OMP_NUM_THREADS-muuttujaa ei tule käyttää kaikissa moniytimisissä R-ajoissa, vaan vain niissä, joissa käytetään OpenMP-säikeitä tukevia paketteja. Oletuksena OMP_NUM_THREADS on asetettu arvoon 1.
Esimerkkieräajoskripti löytyy alta. Tässä lähetämme työn, joka käyttää kahdeksaa ydintä (ja siten kahdeksaa säiettä) yhdellä solmulla. Huomaa, miten sovitamme säikeiden ja ytimien määrän yhteen käyttämällä OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK. Käyttämällä asetusta OMP_PLACES=cores sidomme jokaisen säikeen yhteen ytimeen. Huomaa, että muitakin vaihtoehtoja säieaffiniteetin hallintaan on saatavilla analyysistäsi riippuen.
#!/bin/bash
#SBATCH --job-name=r_multithread
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=2000M
# Load r-env
module load r-env
# Match thread and core numbers
export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
# Place and bind threads to single cores
# Comment the following lines if binding is not desired
export OMP_PLACES=cores
export OMP_PROC_BIND=spread
# Run the R script
srun Rscript --no-save myscript.R
#!/bin/bash
#SBATCH --job-name=r_multithread
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=2000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Match thread and core numbers
export APPTAINERENV_OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
# Thread affinity control
export APPTAINERENV_OMP_PLACES=cores
export APPTAINERENV_OMP_PROC_BIND=close
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
#!/bin/bash
#SBATCH --job-name=r_multithread
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=8 # Each core gives 1.875 GB of memory
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Match thread and core numbers
export APPTAINERENV_OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
# Thread affinity control
export APPTAINERENV_OMP_PLACES=cores
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
Moniytimisessä interaktiivisessa työssä säikeiden määrä voidaan sovittaa automaattisesti ytimien määrään suorittamalla monisäikeinen versio komennoista start-r tai start-rstudio-server:
Multithreaded-valintaruutu.
Usean solmun R-ajot MPI:n avulla
Jos rinnakkainen R-ajo voidaan suorittaa yhden solmun ytimillä, sitä suositellaan aina. Kun ytimiä tarvitaan enemmän, r-env-moduulia voidaan käyttää rinnakkaisten R-ajojen suorittamiseen useiden solmujen yli.
Useilla solmuilla (eli erillisillä tietokoneilla) suoritettavat rinnakkaistehtävät eivät jaa muistia ja tarvitsevat menetelmän viestintään. CSC:n supertietokoneilla solmujen välinen viestintä hoidetaan MPI:llä (Message Passing Interface).
Usean solmun R-ajojen on siksi käytettävä R-paketteja, jotka tukevat usean solmun välistä viestintää MPI:n kautta, kuten future, snow, doMPI (käytettynä yhdessä foreach-paketin kanssa) ja pbdMPI.
Vakiotapa käynnistää MPI-R-ajoja CSC:n supertietokoneilla on käyttää snow-paketin komentoa RMPISNOW R:n käynnistämiseen Rscript-komennon sijaan. Huomaa kuitenkin, että alla on myös esimerkkejä MPI-ajoista, joissa käytetään muita lähestymistapoja.
Useimmat MPI-ajot, mukaan lukien snow-paketilla käynnistetyt, perustuvat viestintämalliin, jossa pääprosessia käytetään muiden prosessien (työntekijöiden) ohjaamiseen. Tämän vuoksi eräajotiedoston
on määritettävä yksi tehtävä enemmän kuin suunniteltu työntekijöiden määrä, koska pääprosessi tarvitsee oman tehtävänsä.
Vaikka eräajotiedosto muistuttaa muuten moniydintyössä käytettävää tiedostoa, korvaamme --cpus-per-task=x
asetuksella --ntasks-per-node=x ja käytämme --nodes-asetusta solmujen määrän määrittämiseen. Työntekijöiden määrä on yleensä ntasks-per-node x nodes - 1. Lisäksi voisimme muokata eräajotiedoston lopussa olevaa srun-komentoa:
--slave-argumentti on valinnainen, ja se estää muun muassa eri prosesseja tulostamasta tervetuloviestiä.
Lisätietoja löytyy MPI-ajojen yleisdokumentaatiosta.
Rmpi-pakettia käyttävissä töissä käytä snow-pakettia (joka on rakennettu Rmpi:n päälle). Pelkkää Rmpi-pakettia käyttävät työt eivät ole saatavilla yhteensopivuusongelmien vuoksi.
future-pakettia käyttävät usean solmun työt
Jotta usean solmun analyysit voidaan suorittaa future-paketilla, valitsemme plan(cluster) ja käynnistämme R:n snow-paketin RMPISNOW-komennolla Rscript-komennon sijaan. Meidän tulee määrittää riittävästi tehtäviä sekä pää- että työntekijäprosesseille.
Esimerkiksi työ, joka tarvitsee mahdollisimman monta työntekijää kahdella solmulla, voitaisiin lähettää future-työnä seuraavasti:
Useita täysiä solmuja:
#!/bin/bash
#SBATCH --job-name=r_future
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=01:00:00
#SBATCH --ntasks-per-node=384 --cpus-per-task=1 # The product should be 384
#SBATCH --nodes=2 # 2 x 384 - 1 = 767 workers
#SBATCH --mem-per-cpu=1000M
# Load r-env
module load r-env
# Run the R script
srun RMPISNOW --no-save --slave -f myscript.R
Osittaisen solmun MPI-työ:
#!/bin/bash
#SBATCH --job-name=r_future
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=01:00:00
#SBATCH --ntasks=8 # 8 CPU cores, 7 workers
#SBATCH --nodes=1
#SBATCH --mem-per-cpu=1000M
#SBATCH --hint=nomultithread
# Load r-env
module load r-env
# Run the R script
srun RMPISNOW --no-save --slave -f myscript.R
#!/bin/bash
#SBATCH --job-name=r_future
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=large
#SBATCH --time=01:00:00
#SBATCH --ntasks-per-node=40
#SBATCH --nodes=2 # 2 x 40 - 1 = 79 workers
#SBATCH --mem-per-cpu=1000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec RMPISNOW --no-save --slave -f myscript.R
#!/bin/bash
#SBATCH --job-name=r_future
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=01:00:00
#SBATCH --ntasks-per-node=128 # 2 x 128 - 1 = 255 workers
#SBATCH --nodes=2
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec RMPISNOW --no-save --slave -f myscript.R
Jotta future toimisi yhdessä snow-paketin kanssa, myös seuraavat rivit tulee sisällyttää R-skriptiin:
snow-pakettia käyttävät usean solmun työt
Jotta usean solmun R-ajo voidaan käynnistää suoraan snow-paketilla siten, että se käyttää mahdollisimman monta työntekijää kahdella solmulla, voisimme lähettää sen seuraavasti. R käynnistetään RMPISNOW-komennolla, ja meidän on määritettävä yksi tehtävä enemmän kuin suunniteltu snow-työntekijöiden määrä, koska pääprosessi tarvitsee oman tehtävänsä.
Useita täysiä solmuja:
#!/bin/bash
#SBATCH --job-name=r_snow
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=01:00:00
#SBATCH --ntasks-per-node=384 --cpus-per-task=1 # The product should be 384
#SBATCH --nodes=2 # 2 x 384 - 1 = 767 workers
#SBATCH --mem-per-cpu=1000M
# Load r-env
module load r-env
# Run the R script
srun RMPISNOW --no-save --slave -f myscript.R
Osittaisen solmun MPI-työ:
#!/bin/bash
#SBATCH --job-name=r_snow
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=01:00:00
#SBATCH --ntasks=8 # 8 CPU cores, 7 workers
#SBATCH --nodes=1
#SBATCH --mem-per-cpu=1000M
# Load r-env
module load r-env
# Run the R script
srun RMPISNOW --no-save --slave -f myscript.R
#!/bin/bash
#SBATCH --job-name=r_snow
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=large
#SBATCH --time=01:00:00
#SBATCH --ntasks-per-node=40
#SBATCH --nodes=2 # 2 x 40 - 1 = 79 workers
#SBATCH --mem-per-cpu=1000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec RMPISNOW --no-save --slave -f myscript.R
#!/bin/bash
#SBATCH --job-name=r_snow
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=01:00:00
#SBATCH --ntasks-per-node=128 # 2 x 128 - 1 = 255 workers
#SBATCH --nodes=2
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec RMPISNOW --no-save --slave -f myscript.R
Vain pääprosessi suorittaa R-skriptin. R-skriptin on sisällettävä kutsu getMPIcluster(), jota käytetään tuottamaan viite klusteriin, joka voidaan sitten välittää muille funktioille. Analyysin valmistuttua klusteri pysäytetään stopCluster()-funktiolla. Esimerkiksi:
doMPI-pakettia käyttävät työt (foreach-paketin kanssa)
foreach-paketti toteuttaa for-silmukan, joka käyttää iteraattoreita ja mahdollistaa rinnakkaisen suorituksen %dopar%-operaattorilla. Rinnakkaisia foreach-silmukoita on mahdollista suorittaa
useilla ytimillä monien eri sovittimien avulla, kuten doParallel sisäänrakennetulle R:n parallel-paketille ja doFuture future-paketille. Usean solmun ja MPI-ajot foreach-paketin kanssa voidaan
suorittaa doMPI-paketin rinnakkaistaustalla.
Toisin kuin snow-pakettia käytettäessä, doMPI-pakettia käyttävät työt käynnistävät yhtä monta R-istuntoa kuin varattuja tehtäviä on, ja kaikki alkavat suorittaa annettua R-skriptiä (tässä kahdeksan).
Osittaisen solmun MPI-työ:
#!/bin/bash
#SBATCH --job-name=r_dompi
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=8
#SBATCH --nodes=1
#SBATCH --mem-per-cpu=1000M
# Load r-env
module load r-env
# Run the R script
srun Rscript --no-save --slave myscript.R
#!/bin/bash
#SBATCH --job-name=r_dompi
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=8
#SBATCH --nodes=1
#SBATCH --mem-per-cpu=1000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
#!/bin/bash
#SBATCH --job-name=r_dompi
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --ntasks=8
#SBATCH --nodes=1
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
On tärkeää
sisällyttää startMPIcluster()-kutsu lähelle R-skriptin alkua, koska kaikki sitä ennen oleva suoritetaan kaikissa käytettävissä olevissa prosesseissa (kun taas vain pääprosessi jatkaa
sen jälkeen). Suorituksen päätyttyä klusteri suljetaan closeCluster()-funktiolla. Tämän jälkeen mpi.quit()-funktiota voidaan käyttää MPI-suoritusympäristön lopettamiseen ja R:n sulkemiseen:
library(doMPI, quietly = TRUE)
cl <- startMPIcluster()
registerDoMPI(cl)
system.time(a <- foreach(i = 1:7) %dopar% system.time(sort(runif(1e7))))
a
closeCluster(cl)
mpi.quit()
pbdMPI-pakettia käyttävät usean solmun työt
Analyyseissä, joissa käytetään pbdMPI-pakettia, jokainen prosessi suorittaa saman kopion ohjelmasta kuin kaikki muutkin prosessit, mutta omalla datallaan. Toisin sanoen erillistä pääprosessia ei ole kuten snow- tai doMPI-paketeissa. pbdMPI-pakettia käyttävien eräajojen suorittaminen voidaan tehdä Rscript-komennolla ilman snow-pakettia. Esimerkiksi voisimme lähettää työn, joka käyttää kaikkia kahden solmun ytimiä (siten, että puolet tehtävien kokonaismäärästä kohdistetaan kummallekin solmulle):
Useita täysiä solmuja:
#!/bin/bash
#SBATCH --job-name=r_pbdmpi
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=00:05:00
#SBATCH --ntasks-per-node=384 --cpus-per-task=1 # The product should be 384
#SBATCH --nodes=2
#SBATCH --mem-per-cpu=2000M
# Load r-env
module load r-env
# Run the R script
srun Rscript --no-save --slave myscript.R
#!/bin/bash
#SBATCH --job-name=r_pbdmpi
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=large
#SBATCH --time=00:05:00
#SBATCH --ntasks-per-node=40
#SBATCH --nodes=2
#SBATCH --mem-per-cpu=2000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save --slave myscript.R
#!/bin/bash
#SBATCH --job-name=r_pbdmpi
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=test
#SBATCH --time=00:05:00
#SBATCH --ntasks-per-node=128
#SBATCH --nodes=2
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Run the R script
srun apptainer_wrapper exec Rscript --no-save --slave myscript.R
Esimerkkinä tätä eräajotiedostoa voitaisiin käyttää suorittamaan seuraava "hello world" -skripti (alkuperäinen versio saatavilla pbdMPI-paketin Github-repositoriosta). init()-funktio alustaa MPI-kommunikaattorit, kun taas finalize()-funktiota käytetään niiden sulkemiseen ja R:n lopettamiseen.
library(pbdMPI, quietly = TRUE)
init()
message <- paste("Hello from rank", comm.rank(), "of", comm.size())
comm.print(message, all.rank = TRUE, quiet = TRUE)
finalize()
OpenMP / MPI -hybridityöt
Sen lisäksi, että monisäikeisiä R-ajoja suoritetaan yhdellä solmulla, niitä voidaan ajaa myös useilla solmuilla. Tällöin on määritettävä seuraavien lukumäärä:
-
Solmut (
--nodes) -
MPI-prosessit per solmu (
--ntasks-per-node) -
Kunkin MPI-prosessin käyttämät OpenMP-säikeet (
--cpus-per-task)
Kun nämä luetellaan eräajotiedostossa, huomaa, että --ntasks-per-node × --cpus-per-task on oltava pienempi tai yhtä suuri kuin yhdellä solmulla käytettävissä olevien ytimien enimmäismäärä. Suurissa usean solmun töissä kannattaa pyrkiä käyttämään täysiä solmuja eli kaikkia kunkin solmun ytimiä. Sopivan OpenMP-säiemäärän valinnan lisäksi optimaalisen MPI-prosessien määrän ja jaon tunnistaminen vaatii kokeiluja, koska nämä ovat työkohtaisia.
Esimerkkinä OpenMP / MPI -hybridityöstä alla oleva lähetys käyttäisi yhteensä neljää MPI-prosessia (kaksi tehtävää per solmu ja kaksi varattua solmua), ja kukin prosessi käyttäisi useita OpenMP-säikeitä (--cpus-per-task). Kokonaisuudessaan työ käyttäisi --cpus-per-task × --ntasks-per-node × --nodes ydintä. Kuten yhdellä solmulla suoritettavissa monisäikeisissä töissä, säikeiden ja ytimien määrä sovitetaan yhteen käyttämällä OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}. Käytämme myös samoja muuttujia säieaffiniteetin hallintaan.
#!/bin/bash
#SBATCH --job-name=r_multithread_multinode
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=medium
#SBATCH --time=00:05:00
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=25
#SBATCH --mem-per-cpu=2000M
# Load r-env
module load r-env
# Match thread and core numbers
export OMP_NUM_THREADS=${SLURM_CPUS_PER_TASK:-1}
# Place and bind threads to single cores
# Comment the following lines if binding is not desired
export OMP_PLACES=cores
export OMP_PROC_BIND=spread
# Run the R script
srun Rscript --no-save myscript.R
#!/bin/bash
#SBATCH --job-name=r_multithread_multinode
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=test
#SBATCH --time=00:05:00
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=8
#SBATCH --mem-per-cpu=2000
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Match thread and core numbers
export APPTAINERENV_OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
# Thread affinity control
export APPTAINERENV_OMP_PLACES=cores
export APPTAINERENV_OMP_PROC_BIND=close
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
#!/bin/bash
#SBATCH --job-name=r_multithread_multinode
#SBATCH --account=<project>
#SBATCH --output=output_%j.txt
#SBATCH --error=errors_%j.txt
#SBATCH --partition=test
#SBATCH --time=00:05:00
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=2
#SBATCH --cpus-per-task=64 # ntasks-per-node x cpus-per-task should equal 128
# Load r-env
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Match thread and core numbers
export APPTAINERENV_OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
# Thread affinity control
export APPTAINERENV_OMP_PLACES=cores
# Run the R script
srun apptainer_wrapper exec Rscript --no-save myscript.R
Laajamittaiset taulukkotyöt GNU parallelin avulla
Suuremmissa taulukkotöissä, joihin liittyy paljon pieniä riippumattomia ajoja, voisimme harkita seuraavaa esimerkkiä. Oletetaan, että meillä on yhteensä 1500 ajoa, jotka haluaisimme suorittaa. Meillä on myös lista (mylist.txt), jossa on yksilölliset tunnisteet jokaiselle ajolle ja joita haluamme käyttää osana R-skriptiä oikean analysoitavan datajoukon hakemiseen. Lista on järjestetty riveittäin näin:
Jotta analyysi voitaisiin suorittaa tehokkaasti, voisimme hyödyntää GNU parallel -ohjelman sisältävää moduulia ajojen "ajastamiseen" taulukkotyön sisällä. Alla olevassa eräajoskriptissä on muutama yksityiskohta, joihin kannattaa kiinnittää huomiota:
-
Tapa, jolla ajot jaetaan taulukoihin, on tapauskohtainen ja vaatii manuaalista laskentaa. Tässä esimerkissä, koska
mylist.txtsisältää 1500 tunnistetta ja käytämme 10 taulukkoa, on päätetty kohdistaa 150 ajoa per taulukko. -
Käytämme asetusta
-j $SLURM_CPUS_PER_TASK -k, jotta GNU parallel pitää 4 sovellusta käynnissä rinnakkain ja samalla varmistaa, että työn tulostusjärjestys vastaa syötteen järjestystä. Samanaikaisten rinnakkaisten sovellusten määrä määritetään asetuksella--cpus-per-task. -
Todellisessa analyysissä tarvitsisimme todennäköisesti paljon enemmän aikaa ja muistia (sen mukaan, mitä teemme R-skriptissä).
#!/bin/bash
#SBATCH --job-name=r_array_gnupara
#SBATCH --account=<project>
#SBATCH --output=output_%j_%a.txt
#SBATCH --error=errors_%j_%a.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --array=0-9
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=4
#SBATCH --mem-per-cpu=2000M
# Load parallel and r-env
module load parallel
module load r-env
# Split runs into arrays and run the R script
(( from_run = SLURM_ARRAY_TASK_ID * 150 + 1 ))
(( to_run = SLURM_ARRAY_TASK_ID * 150 + 150 ))
sed -n "${from_run},${to_run}p" mylist.txt | \
parallel -j $SLURM_CPUS_PER_TASK -k \
Rscript --no-save myscript.R \
$SLURM_ARRAY_TASK_ID
#!/bin/bash
#SBATCH --job-name=r_array_gnupara
#SBATCH --account=<project>
#SBATCH --output=output_%j_%a.txt
#SBATCH --error=errors_%j_%a.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --array=0-9
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=4
#SBATCH --mem-per-cpu=2000
# Load parallel and r-env
module load parallel
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Split runs into arrays and run the R script
(( from_run = SLURM_ARRAY_TASK_ID * 150 + 1 ))
(( to_run = SLURM_ARRAY_TASK_ID * 150 + 150 ))
sed -n "${from_run},${to_run}p" mylist.txt | \
parallel -j $SLURM_CPUS_PER_TASK -k \
apptainer_wrapper exec Rscript --no-save myscript.R \
$SLURM_ARRAY_TASK_ID
#!/bin/bash
#SBATCH --job-name=r_array_gnupara
#SBATCH --account=<project>
#SBATCH --output=output_%j_%a.txt
#SBATCH --error=errors_%j_%a.txt
#SBATCH --partition=small
#SBATCH --time=00:05:00
#SBATCH --array=0-9
#SBATCH --ntasks=1
#SBATCH --nodes=1
#SBATCH --cpus-per-task=4 # Each core gives 1.875 GB of memory
# Load parallel and r-env
module load parallel
module load r-env
# Clean up .Renviron file in home directory
if test -f ~/.Renviron; then
sed -i '/TMPDIR/d' ~/.Renviron
fi
# Specify a temporary directory path (replace <project> with your project)
echo "TMPDIR=/scratch/<project>" >> ~/.Renviron
# Split runs into arrays and run the R script
(( from_run = SLURM_ARRAY_TASK_ID * 150 + 1 ))
(( to_run = SLURM_ARRAY_TASK_ID * 150 + 150 ))
sed -n "${from_run},${to_run}p" mylist.txt | \
parallel -j $SLURM_CPUS_PER_TASK -k \
apptainer_wrapper exec Rscript --no-save myscript.R \
$SLURM_ARRAY_TASK_ID