Hyppää sisältöön

Docs CSC now features an automatic Finnish translation. Click here for more information.

Warning!

Puhti and Mahti are being decommissioned in stages, and their storage areas will become fully unavailable from 15 October 2026. Clean up unnecessary files and move any data you need to keep by 31 August 2026. See the Roihu data migration guide for instructions on transferring your data to Roihu.

Puhti computing services have been decommissioned and no new jobs are accepted or executed on its compute nodes. Puhti login nodes and storage services are planned to remain available until 15 October 2026.

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.

arrays <- commandArgs(trailingOnly = TRUE)
# or
arrays <- Sys.getenv("SLURM_ARRAY_TASK_ID")

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:

start-r-multithread # or
start-rstudio-server-multithread
Selainkäyttöliittymän RStudiossa moniytiminen R-istunto voidaan asettaa käyttämään useita säikeitä valitsemalla 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:

# MPI jobs started with `snow`
srun RMPISNOW --no-save --slave myscript.R

# MPI jobs started without `snow`
srun Rscript --no-save --slave myscript.R
# MPI jobs started with `snow`
srun apptainer_wrapper exec RMPISNOW --no-save --slave myscript.R

# MPI jobs started without `snow`
srun apptainer_wrapper exec Rscript --no-save --slave myscript.R

--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:

library(future)

cl <- getMPIcluster()
plan(cluster, workers = cl)

# Analysis here

stopCluster(cl)

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:

cl <- getMPIcluster()

funtorun <- function(k) {
  system.time(sort(runif(1e7)))
}

system.time(a <- clusterApply(cl, 1:767, funtorun))
a

stopCluster(cl)
cl <- getMPIcluster()

funtorun <- function(k) {
  system.time(sort(runif(1e7)))
}

system.time(a <- clusterApply(cl, 1:255, funtorun))
a

stopCluster(cl)

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ä:

file1
file2
file3
(...)
fileN

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:

set1
set2
set3
(...)
set1500

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.txt sisä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_TASK kertomaan xargs-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 on parallel -j $SLURM_CPUS_PER_TASK -k.

  • Listan tunnisteet voidaan lukea R-skriptissä komennolla args = commandArgs(trailingOnly=TRUE), jonka jälkeen args[1] antaa taulukon numeron ($SLURM_ARRAY_TASK_ID) ja args[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

Lisätietoja

Suomenkielinen tekoälykäännös

Sisällössä voi esiintyä virheellistä tietoa tekoälykäännöksestä johtuen.

Klikkaa tästä antaaksesi palautetta