Parallel processing in R

· โ˜• 4 min read · โœ๏ธ Hoontaek Lee
๐Ÿท๏ธ
  • #2020
  • #R
  • ๋“ค์–ด๊ฐ€๋ฉฐ

    ์›Œํฌ์Šคํ…Œ์ด์…˜ ์„ฑ๋Šฅ์€ ๊ดด๋ฌผ๊ฐ™์€๋ฐ ํ•  ์ผ ์—†์ด ๋†€๊ณ ๋งŒ ์žˆ๋‹ค.
    ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋กœ ๋นก์„ธ๊ฒŒ ๊ตด๋ ค๋ณด์ž.
    ์ด๋†ˆ๋“ค.
    (ํ•˜์ง€๋งŒ ๋‚ด ์ฝ”๋“œ๊ฐ€ ๋ฉ€ํ‹ฐ์ฝ”์–ด ์‚ฌ์šฉ์œผ๋กœ๋Š” ํ•ด๊ฒฐ๋˜์ง€ ์•Š์„ ์ข…๋ฅ˜์ผ ์ˆ˜ ์žˆ๋‹ค.)

    ํ…œํ”Œ๋ฆฟ

    ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

    1
    2
    3
    
    # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
    
    library(foreach)
    library(doParallel)
    

    foreach๋ฅผ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š๊ณ  foreach::foreach๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด
    %dopar% ํ•จ์ˆ˜๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

    ํด๋Ÿฌ์Šคํ„ฐ ์…‹์—…: ํŒŒํ‹ฐ ๋ชจ์ง‘

    ๊ฒŒ์ž„์—์„œ ๋ ˆ์ด๋“œ ๋›ธ ํŒŒํ‹ฐ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์ž‘์—…์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. (= ์ผ ํ•  ์ฝ”์–ด ๊ตฌ์„ฑ)

    1
    2
    
    # ์ฝ”์–ด ์ˆซ์ž ์„ค์ •
    
    n.cores <- parallel::detectCores() - 1
    

    ๋ช‡ ๋ช… ๋ชจ์ง‘ํ•  ๊ฒƒ์ธ์ง€ ์ •ํ•œ๋‹ค. parallel::detectCores()๋Š” ๊ฐ€์šฉ ์ตœ๋Œ€ ์ฝ”์–ด ์ˆ˜๋ฅผ ์•Œ๋ ค์ค€๋‹ค.
    ๋งˆ์šฐ์Šค ์›€์ง์ด๋Š” ๋“ฑ์€ ๊ธฐ๋ณธ ์ž‘์—…์„ ํ•ด์•ผ ํ•˜๋‹ˆ 1๊ฐœ ๋นผ์ค€๋‹ค.

    1
    2
    3
    
    # ํด๋Ÿฌ์Šคํ„ฐ ์ดˆ๊ธฐํ™”
    
    slave.cluster <- parallel::makeCluster(n.cores)
    doParallel::registerDoParallel(slave.cluster)
    

    parallel::makeCluster(ํŒŒํ‹ฐํฌ๊ธฐ)๋กœ ํŒŒํ‹ฐ(ํด๋Ÿฌ์Šคํ„ฐ)๋ฅผ ๋งŒ๋“ค๊ณ ,
    doParallel::registerDoParallel(ํŒŒํ‹ฐ์ด๋ฆ„)์œผ๋กœ ๋“ฑ๋กํ•˜์ž(์™œ ๋“ฑ๋ก์ด ํ•„์š”ํ• ๊นŒ).

    ์ •ํ™•ํ•œ ๊ธฐ๋Šฅ์€ ๋งค๋‰ด์–ผ์„ ์ฐธ๊ณ ํ•˜์ž.

    ๋ณ€์ˆ˜ ํ• ๋‹น: ์•„์ดํ…œ ์ ๊ฒ€

    ๋ ˆ์ด๋“œ ๋›ฐ๊ธฐ ์ „์— ์žฅ๋น„ ๋‚ด๊ตฌ๋„, ์†Œ๋ชจํ’ˆ ๋“ฑ ์ ๊ฒ€ํ•ด์•ผ ํ•œ๋‹ค.
    ๋ณธ๊ฒฉ์ ์ธ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ ์ˆ˜ํ–‰ ์ „์—๋Š” ํ•„์š”ํ•œ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•œ๋‹ค.
    foreach ์™ธ๋ถ€์—์„œ ์ •์˜ํ•œ ๋ณ€์ˆ˜๋Š” foreach ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉํ•˜์ง€ ๋ชป ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค(์ฐธ๊ณ ).

    1
    2
    3
    
    # ํด๋Ÿฌ์Šคํ„ฐ์— ์ „์—ญ ๋ณ€์ˆ˜ ์ถ”๊ฐ€
    
    parallel::clusterExport(slave.cluster,
                            varlist = c("add", "variables", ...))
    

    ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” foreach ์ˆ˜ํ–‰ํ•˜๋ฉด์„œ ์ฝ์–ด๋“ค์ผ ์ˆ˜ ์žˆ๋‹ค.

    ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ: ๋ ˆ์ด๋“œ

    1
    2
    3
    4
    5
    6
    7
    
    # ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ ์ˆ˜ํ–‰
    
    foreach::foreach(i = 1:18,
                     .combine = rbind,
                     .packages = c("dplyr"),
                     .inorder = TRUE) %dopar% {
                       "์ˆ˜ํ–‰ํ•  ์ฝ”๋“œ"
                     }
    

    ๊ธฐ๋ณธ์ ์ธ for ๊ตฌ๋ฌธ๊ณผ ๋น„์Šทํ•œ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง„๋‹ค.

    • ์นด์šดํ„ฐ์™€ ๋ฒ”์œ„ ์„ค์ •ํ•œ๋‹ค.
    • .combine: foreach์—์„œ๋Š” ์—ฌ๋Ÿฌ ์ฝ”์–ด๊ฐ€ ๋…๋ฆฝ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค.
      .combine์„ ์ด์šฉํ•ด ๊ฐ์ž ๊ฒฐ๊ณผ๋ฅผ ์ทจํ•ฉํ•˜๋Š” ๋ฐฉ์‹์„ ๊ฒฐ์ •ํ•œ๋‹ค.
      • list: ๊ธฐ๋ณธ๊ฐ’. ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ์ด์–ด ๋ถ™์ธ๋‹ค.
      • c: ๋ฒกํ„ฐ ํ˜•ํƒœ๋กœ ์ด์–ด ๋ถ™์ธ๋‹ค.
      • rbind: ์„ธ๋กœ๋กœ ์ด์–ด ๋ถ™์—ฌ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ๋งŒ๋“ ๋‹ค.
      • cbind: ๊ฐ€๋กœ๋กœ ์ด์–ด ๋ถ™์—ฌ ๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ๋งŒ๋“ ๋‹ค.
      • ๋“ฑ๋“ฑ
    • `.packages”: ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์ฑ™๊ฒจ๊ฐ„๋‹ค.
    • .inorder: ์ทจํ•ฉํ•  ๋•Œ ์›๋ž˜ ์ˆœ์„œ(i=1, 2, 3, …)๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด TRUE๋ฅผ ์„ ํƒํ•œ๋‹ค(๊ธฐ๋ณธ๊ฐ’).
    • %dopar%: ๋ฌธ๋ฒ• ์š”์†Œ๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ๋œ๋‹ค. %do%๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹จ์ผ์ฝ”์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค(= for ๊ตฌ๋ฌธ).

    ํด๋Ÿฌ์Šคํ„ฐ ํ•ด์ œ: ํ•ด์‚ฐ

    ๋ณด์ƒ์„ ์ฑ™๊ธด ํ›„์—๋Š” ์ฟจํ•˜๊ฒŒ ํ•ด์‚ฐํ•œ๋‹ค.

    1
    2
    
    # ํด๋Ÿฌ์Šคํ„ฐ ํ•ด์ œ
    
    parallel::stopCluster(slave.cluster)
    

    ์ „์ฒด ์ฝ”๋“œ

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
    
    library(foreach)
    library(doParallel)
    
    # ์ฝ”์–ด ์ˆซ์ž ์„ค์ •
    
    n.cores <- parallel::detectCores() - 1
    
    # ํด๋Ÿฌ์Šคํ„ฐ ์ดˆ๊ธฐํ™”
    
    slave.cluster <- parallel::makeCluster(n.cores)
    doParallel::registerDoParallel(slave.cluster)
    
    # ํด๋Ÿฌ์Šคํ„ฐ์— ์ „์—ญ ๋ณ€์ˆ˜ ์ถ”๊ฐ€
    
    parallel::clusterExport(slave.cluster,
                            varlist = c("add", "variables", ...))
    
    # ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ ์ˆ˜ํ–‰
    
    foreach::foreach(i = 1:18,
                     .combine = rbind,
                     .packages = c("dplyr"),
                     .inorder = TRUE) %dopar% {
                       "์ˆ˜ํ–‰ํ•  ์ฝ”๋“œ"
                     }
    
    # ํด๋Ÿฌ์Šคํ„ฐ ํ•ด์ œ
    
    parallel::stopCluster(slave.cluster)
    

    ์ƒ๊ฐํ•ด๋ณผ ๊ฒƒ๋“ค

    ๊ฐ ๋ฐ˜๋ณต ์ž‘์—…์€ ์„œ๋กœ ๋…๋ฆฝ์ ์ด์–ด์•ผ ํ•œ๋‹ค?

    foreach๋Š” ๋ฐ˜๋ณต๋ฌธ์ด ํ•ด์•ผ ํ•  ์ž‘์—…์„ ์—ฌ๋Ÿฌ ์ฝ”์–ด์— ๋‚˜๋ˆ ์„œ ๋น ๋ฅด๊ฒŒ ์ˆ˜ํ–‰ํ•œ ํ›„ ๊ฐ ๊ฒฐ๊ณผ๋ฅผ ์ทจํ•ฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค.
    ๋•Œ๋ฌธ์—, ๋ฐ˜๋ณต ์ž‘์—… ๊ฐ„์— ๊ฐ„์„ญ์ด ์—†์–ด์•ผ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.
    ์˜ˆ๋ฅผ ๋“ค์–ด, i=1์ผ๋•Œ์˜ ๊ฒฐ๊ณผ๊ฐ€ i=2์ผ๋•Œ ์ž‘์—…์— ์‚ฌ์šฉ๋ผ์•ผ ํ•˜๋Š”๋ฐ,
    ๋‘ ์ž‘์—…์„ ์„œ๋กœ ๋‹ค๋ฅธ ์ฝ”์–ด๊ฐ€ ์ˆ˜ํ–‰ํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค์ง€ ์•Š์„ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค.
    ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ์—์„œ
    ์ž‘์—…์ด ์—ฌ๋Ÿฌ ์ฝ”์–ด์— ์–ด๋–ป๊ฒŒ ๋ถ„๋ฐฐ๋˜๋Š”์ง€,
    ์ฝ”์–ด ๊ฐ„์— ๊ฒฐ๊ณผ ๊ณต์œ ๊ฐ€ ๊ฐ€๋Šฅํ•œ์ง€,
    ์—ฌ๋Ÿฌ ์ฝ”์–ด๊ฐ€ ํ•œ ์ž‘์—…์„ ํ•จ๊ป˜ ๋งก์„ ์ˆ˜ ์žˆ๋Š”์ง€,
    ์ž‘์—… ๋‚ด์šฉ์— ๋งž์ถฐ์„œ ์•Œ์•„์„œ ์Šค๋งˆํŠธํ•˜๊ฒŒ ๋ถ„๋ฐฐ๋˜๋Š”์ง€…์™€ ๊ฐ™์€ ์›๋ฆฌ๋ฅผ ์•Œ๋ฉด ๋” ์ดํ•ดํ•˜๊ธฐ ์ˆ˜์›”ํ•  ๊ฒƒ ๊ฐ™๋‹ค.

    ์ค‘์ฒฉ ๋ฐ˜๋ณต๋ฌธ ๊ตฌ์กฐ์ธ ๊ฒฝ์šฐ?

    ๋‚ด๊ฐ€ ์ฒ˜์Œ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•œ ์ฝ”๋“œ๋Š” ๋Œ€๋žต ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

    for(i in 1:csvํŒŒ์ผ๊ฐœ์ˆ˜){
      csv ์ฝ์–ด์„œ ๊ฐ€๊ณต
      
      # ๋ณ‘๋ชฉ ๊ตฌ๊ฐ„ 1
      for(s in 1:๊ด€์ธก์ง€์ ์ˆ˜){
        
      }
      
      # ๋ณ‘๋ชฉ ๊ตฌ๊ฐ„ 2
      for(s in 1:๊ด€์ธก์ง€์ ์ˆ˜){
        
      }
      
      ๊ฒฐ๊ณผ ์ทจํ•ฉ ๋ฐ ๊ฐ€๊ณต
      
      ๊ฒฐ๊ณผ ์ €์žฅ
    }
    

    csvํŒŒ์ผ ํ•˜๋‚˜ํ•˜๋‚˜๋งˆ๋‹ค ์ผ๋ จ์˜ ์ž‘์—… ์ˆ˜ํ–‰ ํ›„ ๊ฒฐ๊ณผ๋ฅผ ์ €์žฅํ•˜๋Š” ์ž‘์—…์„ ๋ฐ˜๋ณตํ•˜๋Š” ์ฝ”๋“œ๋‹ค.
    ๋Œ€๋ถ€๋ถ„ ์ˆ˜ํ–‰์‹œ๊ฐ„์€ ๋‚ด๋ถ€ ๋ฐ˜๋ณต๋ฌธ์ด ์ฐจ์ง€ํ•œ๋‹ค.
    ๊ฐ csv ํฌ๊ธฐ๋Š” 300MB~2000MB ์ •๋„๋กœ ๋น„๋Œ€ํ•˜๊ณ (์ค‘์•™๊ฐ’ 1000MB ์ •๋„), ์ด 99๊ฐœ๋‹ค.
    ๋‚ด ์›Œํฌ์Šคํ…Œ์ด์…˜์˜ CPU ์ฝ”์–ด๋Š” 32๊ฐœ์ด๊ณ  ๋žจ๋„ 256GB๋กœ ์ถฉ๋ถ„ํ•˜๋‹ค.

    ๋‹จ์ผ์ฝ”์–ด๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ 1500MB ํŒŒ์ผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ 30๋ถ„ ์ •๋„ ๊ฑธ๋ ธ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด…
    ๋ณ‘๋ ฌ์ฒ˜๋ฆฌํ•ด์„œ 31์ฝ”์–ด๋กœ ๋Œ๋ฆฌ๋ฉด 31๋ฐฐ๊นŒ์ง€๋Š” ์•„๋‹ˆ๋”๋ผ๋„ ๊ธˆ๋ฐฉ ๋๋‚˜๊ฒ ๊ฑฐ๋‹ˆ ์ƒ๊ฐํ–ˆ์ง€๋งŒ, ์˜คํžˆ๋ ค ๋” ์˜ค๋ž˜ ๊ฑธ๋ ธ๋‹ค.

    ์™œ ๋” ์˜ค๋ž˜ ๊ฑธ๋ ธ์„๊นŒ?: ๋ฌด์ž‘์ • foreach()๋กœ ๊ฐ์‹ธ์ง€ ๋ง๊ธฐ!

    ์•„๋ž˜๋Š” ๋‚ด๊ฐ€ ์ฒ˜์Œ ์‹œ๋„ํ–ˆ๋˜ ์ฝ”๋“œ๋‹ค.

    foreach(){
    
    for(i in 1:csvํŒŒ์ผ๊ฐœ์ˆ˜){
      csv ์ฝ์–ด์„œ ๊ฐ€๊ณต
      
      # ๋ณ‘๋ชฉ ๊ตฌ๊ฐ„ 1
      for(s in 1:๊ด€์ธก์ง€์ ์ˆ˜){
        
      }
      
      # ๋ณ‘๋ชฉ ๊ตฌ๊ฐ„ 2
      for(s in 1:๊ด€์ธก์ง€์ ์ˆ˜){
        
      }
      
      ๊ฒฐ๊ณผ ์ทจํ•ฉ ๋ฐ ๊ฐ€๊ณต
      
      ๊ฒฐ๊ณผ ์ €์žฅ
    }
    
    
    }
    

    ์ฒซ ๋ฒˆ์งธ for()๋ฅผ foreach()๋กœ ๋ฐ”๊ฟจ์–ด์•ผ ํ—€๋Š”๋ฐ foreach()์— ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ํ†ต์งธ๋กœ ๋„ฃ์–ด๋ฒ„๋ ธ๋‹ค.
    ๋ฐ˜๋ณต๋ฌธ์„ ํ•˜๋‚˜ ๋” ์”Œ์› ์œผ๋‹ˆ ์—„์ฒญ ๋Š๋ ค์ง„ ๊ฒƒ์ด๋‹ค.

    ์–ด๋–ค ๋ฐ˜๋ณต๋ฌธ์„ ๋ณ‘๋ ฌ์ฒ˜๋ฆฌํ• ์ง€ ์„ ํƒํ•ด์•ผ ํ•œ๋‹ค.

    ์•„๋ฌดํŠผ, foreach์—๋Š” ์ค‘์ฒฉ ๋ฐ˜๋ณต๋ฌธ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค.

    1
    2
    3
    4
    
    foreach(b=bvec, .combine='cbind') %:%
      foreach(a=avec, .combine='c') %dopar% {
        sim(a, b)
      }
    

    ์œ„์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•œ๋‹ค.

    • %:%: nesting operator. foreach๋ฅผ ๊ฒน๊ฒน์ด ์‚ฌ์šฉํ•  ๋•Œ,
      ๋ฐ”๊นฅ์˜ foreach์—์„œ %dopar%, %do% ๋Œ€์‹  ์จ์ค€๋‹ค.

    ์ฐธ๊ณ ์ž๋ฃŒ ๋ฐ ์ฝ์„๊ฑฐ๋ฆฌ

    Share on

    Hoontaek Lee
    WRITTEN BY
    Hoontaek Lee
    Tree-Forest-Climate Researcher

    What's on this Page