-
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 noodia.
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-ajotöihin.
Voit myös tutustua asiaankuuluvien 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 hyödyntämään useita ytimiä (piilotettu rinnakkaisuus), 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 Service Deskiin 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 suoritettavien Slurm-töiden lähettämiseen. Alla oleva esimerkkiskripti lähettäisi työn, joka sisältää kymmenen toisistaan riippumatonta alitehtävää small-osiolle siten, että kukin 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ää määrittämään esimerkiksi, mikä datajoukko tai parametrijoukko analysoidaan kussakin taulukossa.
Taulukkotyöt soveltuvat parhaiten tapauksiin, joissa kukin alitehtävä kestää yli noin 30 minuuttia ja alitehtävien määrä on enintään 400. Suurempiin taulukkoasetelmiin, joissa alitehtävät ovat lyhyempiä, katso alla oleva esimerkki monista pienistä riippumattomista ajoista xargsilla.
Moniydintyöt
Seuraava eräajotiedosto näyttää, miten lähetetään työ, joka käyttää useita ytimiä yhdellä noodilla.
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
#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ä käyttöönotolla. future-paketti tarjoaa kehikon R-ajoille, jotka käyttävät futureja (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ä noodilla, plan(multisession) ja plan(multicore) ovat sopivia. 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) soveltuu töihin, joissa käytetään useita noodeja (katso alla).
Lähetettäessä työtä, joka käyttää multisession- tai multicore-futureja, tulee määrittää yksi noodi (--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 olevissa usean noodin R-töissä MPI:n kanssa) 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-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ä noodilla. 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
#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 start-r- tai start-rstudio-server-komennosta:
Multithreaded-valintaruutu.
Usean noodin R-työt MPI:n kanssa
Jos rinnakkainen R-työ voidaan suorittaa yhden noodin ytimillä, sitä suositellaan aina. Kun ytimiä tarvitaan enemmän, r-env-moduulia voidaan käyttää rinnakkaisten R-töiden suorittamiseen useiden noodien yli.
Useilla noodeilla (eli erillisillä tietokoneilla) suoritettavat rinnakkaistehtävät eivät jaa muistia ja tarvitsevat menetelmän viestintään. CSC:n supertietokoneilla noodien välinen viestintä hoidetaan MPI:llä (Message Passing Interface).
Usean noodin R-töissä on siksi käytettävä R-paketteja, jotka tukevat usean noodin viestintää MPI:n kautta , kutenfuture, snow, doMPI (käytettynä 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ää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äytettyä tiedostoa, korvaamme --cpus-per-task=x
asetuksella --ntasks-per-node=x ja käytämme --nodes-asetusta noodien 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.
Rmpi-pakettia käyttävissä töissä käytä snow-pakettia (joka on rakennettu Rmpi-paketin päälle). Pelkkää Rmpi-pakettia käyttävät työt eivät ole saatavilla yhteensopivuusongelmien vuoksi.
future-pakettia käyttävät usean noodin työt
Jotta voimme suorittaa usean noodin analyysejä 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össä, joka tarvitsee mahdollisimman monta työntekijää kahdella noodilla, voisimme lähettää future-työn seuraavasti:
Multiple full nodes:
#!/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
Partial node MPI job:
#!/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 snow-paketin kanssa, myös seuraavat rivit tulee sisällyttää R-skriptiin:
snow-pakettia käyttävät usean noodin työt
Jotta voisimme käynnistää usean noodin R-työn suoraan snow-paketilla, joka tarvitsee mahdollisimman monta työntekijää kahdella noodilla, 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ä.
Multiple full nodes:
#!/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
Partial node MPI job:
#!/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 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 (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. Usean noodin ja MPI-työt foreach-paketin kanssa voidaan
suorittaa 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).
Partial node MPI job:
#!/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ä edeltävä suoritetaan kaikissa käytettävissä olevissa prosesseissa (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()
pbdMPI-pakettia käyttävät usean noodin työt
Analyyseissä, joissa käytetään pbdMPI-pakettia, jokainen prosessi suorittaa saman kopion ohjelmasta kuin kaikki muutkin prosessit, mutta käsittelee omaa dataansa. 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 noodin ytimiä (siten, että puolet tehtävien kokonaismäärästä kohdistetaan kummallekin noodille):
Multiple full nodes:
#!/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-töitä suoritetaan yhdellä noodilla, niitä voidaan ajaa myös useilla noodeilla. Tällaisissa tapauksissa on määritettävä seuraavien määrä:
-
noodit (
--nodes) -
MPI-prosessit per noodi (
--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ä noodilla käytettävissä olevien ytimien enimmäismäärä. Suurissa usean noodin töissä pyri käyttämään täysiä noodeja eli kaikkia kunkin noodin 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 noodi ja kaksi varattua noodia), 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ä noodilla 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 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=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
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 kertoaksemme xargs-komennolle, että sen tulee lukea syötteestä
mylist.txt yksi rivi 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 viittaa 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
#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
xargs -n 1 -P $SLURM_CPUS_PER_TASK \
apptainer_wrapper exec 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 taulukoihin.
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 datajoukon hakemiseen analyysiä varten. Lista on järjestetty riveittäin näin:
Jotta voisimme suorittaa analyysimme 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 per taulukko. -
Käytämme
-n 1 -P $SLURM_CPUS_PER_TASKkertoaksemmexargs-komennolle, että sen tulee lukea syötteestä (tietystämylist.txt-tiedoston osasta) yksi rivi kerrallaan ja suorittaa 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
```bash
!/bin/bash
SBATCH --job-name=r_array_gnupara
SBATCH --account=
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 with your project)
echo "TMPDIR=/scratch/
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 ```
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