Simon Barthelmé (GIPSA-lab, CNRS)
There are several ways of doing things in parallel with imager. One of them is to use of R’s many packages for doing things in parallel (parallel, futures, etc.). The other one is to take advantage of CImg’s ative use of OpenMP.
Parallelising from R is very easy (provided that what you want to do actually parallelises). Something that does parallelise easily is to run the same operations on different images, or on different image channels.
library(parallel)
#A really big image
im <- boats %>% imresize(8)
#Rank pixels in each image channel
#Serial version
fun <- function() imsplit(im,"c") %>% lapply(rank)
system.time(fun())
## user system elapsed
## 7.420 0.148 7.569
#Parallel version: use mclapply
fun.par <- function() imsplit(im,"c") %>% mclapply(rank,mc.cores=2)
system.time(fun.par())
## user system elapsed
## 2.608 0.200 5.297
Many CImg operations are parallelised natively. The parallelisation is optional and is only activated starting from a certain image size. The speed-ups are sublinear, meaning that unless your image is gigantic you won’t gain much from throwing 200 cores at a problem.
By default OpenMP will grab all the CPU cores it can. You can control how many cores are accessed using OpenMPController::omp_set_num_threads
:
library(imager)
library(microbenchmark)
#Let's do a big convolution
a <- boats
b <- imnoise(30,30)
fun <- function() convolve(a,b)
#No parallelisation
OpenMPController::omp_set_num_threads(1)
## [[1]]
## [1] 1
microbenchmark(fun(),times=15)
## Unit: milliseconds
## expr min lq mean median uq max neval
## fun() 321.2343 321.4757 322.0489 321.7571 322.4195 323.8651 15
#2 cores
OpenMPController::omp_set_num_threads(2)
## [[1]]
## [1] 2
microbenchmark(fun(),times=15)
## Unit: milliseconds
## expr min lq mean median uq max neval
## fun() 161.9575 163.2676 165.7719 164.66 165.988 175.4034 15
#3 cores, etc.
OpenMPController::omp_set_num_threads(3)
## [[1]]
## [1] 3
microbenchmark(fun(),times=15)
## Unit: milliseconds
## expr min lq mean median uq max neval
## fun() 116.8546 129.1712 137.0371 132.9894 143.9082 171.7494 15
If CImg’s parallelisation doesn’t seem to work on your machine, it’s probably because you compiled the package with clang, which has patchy support for OpenMP. Recompile using gcc if possible.
Here’s a simple benchmark: medianblur can be parallelised across image channels. First, the R version using mclapply:
OpenMPController::omp_set_num_threads(1)
## [[1]]
## [1] 1
fun.R <- function() imsplit(boats,"c") %>% mclapply(function(v) medianblur(v,50),mc.cores=3)
microbenchmark(fun.R(),times=20)
## Unit: seconds
## expr min lq mean median uq max neval
## fun.R() 2.800479 2.807245 2.86281 2.811756 2.858016 3.381394 20
Second, CImg’s native version:
OpenMPController::omp_set_num_threads(3)
## [[1]]
## [1] 3
fun.nat <- function() medianblur(boats,50)
microbenchmark(fun.nat(),times=20)
## Unit: seconds
## expr min lq mean median uq max neval
## fun.nat() 2.76762 2.770584 2.771579 2.771564 2.772501 2.776503 20
Pros and cons of using native parallelisation:
Pros and cons of parallelisation from R:
Note that both types of parallelisation can be combined if you can spread the load over several machines.