-
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öt sekä MPI:hin (Message Passing Interface) perustuvat työt, 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-töihin.
Voit myös tutustua asiaankuuluviin R-pakettien käyttöohjeisiin ja 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 käyttämään useita ytimiä (piilorinnakkaisuus), tai R-skriptisi on kirjoitettu hyödyntämään useita ytimiä. Jos et ole varma, voiko R-koodisi käyttää 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 - ottaa yhteyttä CSC:n asiakastukeen saadaksesi neuvoja
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 ajettavien Slurm-töiden lähettämiseen. Alla oleva esimerkkiskripti lähettäisi työn, joka sisältää kymmenen toisistaan riippumatonta osatehtävää small-osiolle 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 # 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 taulukkoajossa.
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 monista pienistä riippumattomista ajoista xargsilla.
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). Ajoaika 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 # 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-töitä vähäisellä valmistelulla. future-paketti tarjoaa kehikon future-olioita käyttäville R-töille (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 ilmoittama ydinmää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-töistä, joissa käytetään plan(multicore)- ja plan(cluster)-asetuksia (kuten alla kohdassa monisolmuiset R-työt MPI:llä) 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äikeisyydelle. Myös muuntyyppisiä R-paketteja käyttävät työt voivat hyötyä monisäikeisyydestä analyysistä riippuen. Esimerkiksi monisäikeisyys voi nopeuttaa lineaarialgebran rutiineja. Selvittääksesi, hyödyttääkö monisäikeisyys 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-töissä, 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ä 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 # 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.
Monisolmuiset R-työt MPI:llä
Jos rinnakkainen R-työ voidaan suorittaa yhden solmun ytimillä, sitä suositellaan aina. Kun tarvitaan enemmän ytimiä, r-env-moduulia voidaan käyttää rinnakkaisten R-töiden suorittamiseen useiden solmujen yli.
Useilla solmuilla (eli erillisillä tietokoneilla) ajettavat 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).
Monisolmuisten R-töiden on siksi käytettävä R-paketteja, jotka tukevat monisolmuista viestintää MPI:n kautta, kuten future, snow, doMPI (käytettynä yhdessä foreach-paketin kanssa) ja pbdMPI.
Vakiotapa käynnistää MPI-R-työt 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-töistä, joissa käytetään muita lähestymistapoja.
Useimmat MPI-työt, mukaan lukien snow-paketilla käynnistetyt, perustuvat viestintämalliin, jossa pääprosessi ohjaa muita prosesseja (työntekijöitä). Tämän vuoksi eräajotiedostossa
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-töiden yleisestä dokumentaatiosta.
Jos työ käyttää Rmpi-pakettia, käytä snow-pakettia (joka on rakennettu Rmpi:n päälle). Pelkkää Rmpi-pakettia käyttävät työt eivät ole käytettävissä yhteensopivuusongelmien vuoksi.
Monisolmuiset työt future-paketilla
Jotta monisolmuisia analyysejä voidaan ajaa future-paketilla, valitsemme plan(cluster) ja käynnistämme R:n snow-paketin komennolla RMPISNOW 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=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:
Monisolmuiset työt snow-paketilla
Jotta monisolmuinen R-työ voidaan käynnistää suoraan snow-paketilla siten, että se käyttää mahdollisimman monta työntekijää kahdella solmulla, sen voisi lähettää 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=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 ja joka voidaan sitten välittää muille funktioille. Analyysin valmistuttua klusteri pysäytetään stopCluster()-funktiolla. Esimerkiksi:
doMPI-pakettia käyttävät työt (yhdessä 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-paketille parallel ja doFuture paketille future. Monisolmuisia ja MPI-töitä foreach-paketin kanssa voidaan
ajaa doMPI-paketin rinnakkaistaustajärjestelmällä.
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
# 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ä edeltävä suoritetaan kaikilla käytettävissä olevilla prosesseilla (kun taas vain pääprosessi jatkaa
sen jälkeen). Valmistuttuaan 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()
Monisolmuiset työt pbdMPI-paketilla
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=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 on saatavilla pbdMPI-paketin Github-repositoriosta). init()-funktio alustaa MPI-kommunikaattorit, kun taas finalize()-funktiota käytetään niiden sulkemiseen ja R:stä poistumiseen.
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-töitä suoritetaan yhdellä solmulla, niitä voidaan ajaa myös useilla solmuilla. Tällaisissa tapauksissa on määritettävä seuraavien määrä:
-
Solmut (
--nodes) -
MPI-prosessit solmua kohden (
--ntasks-per-node) -
Kullekin MPI-prosessille käytettävä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 monisolmuisissa 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ää solmua kohden 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 ajettavissa 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 samoja muuttujia myös 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=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
Monet pienet riippumattomat ajot xargsilla
Kun haluamme suorittaa suuren määrän pieniä, samankaltaisia ja toisistaan riippumattomia tehtäviä, ne kannattaa pakata yhteen tai muutamaan Slurm-työhön sen sijaan, että ne lähetettäisiin monina yksittäisinä töinä. Yleinen tapaus on saman R-skriptin ajaminen suurelle määrälle tiedostoja. Jos kunkin tiedoston tai tiedostoryhmän tehtävä kestää 30 minuuttia tai pidempään, yllä esitellyt taulukkotyöt ovat hyvä valinta. Kun yksittäiset tehtävät ovat lyhyempiä, voimme sen sijaan hyödyntää xargsia.
Seuraavassa esimerkissä mylist.txt sisältää käsiteltävien N tiedoston tunnisteet. Lista näyttää tältä:
Käytämme -n 1 -P $SLURM_CPUS_PER_TASK kertomaan xargs-komennolle, että se lukee syötteestä
mylist.txt yhden rivin kerrallaan. Samanaikaisten rinnakkaisten sovellusten määrä määritetään asetuksella --cpus-per-task,
ja tässä asetamme 4 tunnistetta käsiteltäväksi kerralla.
Käsiteltävänä oleva tunniste voidaan lukea R-skriptissä komennolla args = commandArgs(trailingOnly=TRUE)
ja sen jälkeen args[1], joka osoittaa kyseiseen tunnisteeseen.
Todellisessa analyysissä tarvitsisimme todennäköisesti paljon enemmän aikaa ja muistia (sen mukaan, mitä teemme R-skriptissämme).
#!/bin/bash
#SBATCH --job-name=r_xargs
#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=4
#SBATCH --mem-per-cpu=2000M
# Load r-env
module load r-env
xargs -n 1 -P $SLURM_CPUS_PER_TASK \
Rscript --no-save myscript.R < mylist.txt
#!/bin/bash
#SBATCH --job-name=r_xargs
#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=4 # 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
xargs -n 1 -P $SLURM_CPUS_PER_TASK \
apptainer_wrapper exec Rscript --no-save myscript.R < mylist.txt
Laajamittaiset taulukkotyöt xargsilla tai GNU parallelilla
Hyvin suurille määrille pieniä riippumattomia ajoja voimme yhdistää xargsin taulukkotöihin.
Tässä annamme pienemmän esimerkin havainnollistamaan lähestymistapaa R-skriptin kanssa. 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 voidaan suorittaa tehokkaasti, voisimme hyödyntää peruskäyttöjärjestelmän tarjoamaa xargsia (tai moduulia, joka sisältää GNU Parallelin) "aikatauluttamaan", miten ajot suoritetaan 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 taulukkoa kohden. -
Käytämme
-n 1 -P $SLURM_CPUS_PER_TASKkertomaanxargs-komennolle, että se lukee syötteestä (tietystämylist.txt-tiedoston osasta) yhden rivin kerrallaan ja ajaa 4 sovellusta rinnakkain. Samanaikaisten rinnakkaisten sovellusten määrä määritetään asetuksella--cpus-per-task. GNU Parallelia käytettäessä vastaava komento onparallel -j $SLURM_CPUS_PER_TASK -k. -
Listan tunnisteet voidaan lukea R-skriptissä komennolla
args = commandArgs(trailingOnly=TRUE), jonka jälkeenargs[1]antaa taulukon numeron ($SLURM_ARRAY_TASK_ID) jaargs[2]tässä taulukossa analysoitavat tunnisteet. -
Todellisessa analyysissä tarvitsisimme todennäköisesti paljon enemmän aikaa ja muistia (sen mukaan, mitä teemme R-skriptissämme).
#!/bin/bash
#SBATCH --job-name=r_array_xargs
#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 r-env
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 | \
xargs -n 1 -P $SLURM_CPUS_PER_TASK \
Rscript --no-save myscript.R \
$SLURM_ARRAY_TASK_ID
Using GNU parallel:
#!/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