230 likes | 370 Views
How to Speed Up Your R Code With Virtually No Effort. Alec Stephenson. What I am Talking About. Basic Principals system.time(), benchmark() in rbenchmark Profiling R Code Rprof() Parallel Programming mclapply() parLapply() in parallel Using R With Amazon EC2.
E N D
How to Speed Up Your R Code With Virtually No Effort Alec Stephenson
What I am Talking About • Basic Principals system.time(), benchmark() in rbenchmark • Profiling R Code Rprof() • Parallel Programming mclapply() parLapply() in parallel • Using R With Amazon EC2
What I am Not Talking About • Writing complied code (e.g. C/C++) • Parallelizing compiled code (e.g. OpenMP) • Byte code compiler for R • Is code optimization worth your time? • Is code optimization worth turning your code into an unreadable mess?
Basic Principals • Vectorize rather than loop • Allocate memory up front • Get to the compiled code faster .C .Fortran .External .Internal .Call .Primitive • R can be unpredictable
Vectorize rather than loop x <- rnorm(1e06); y <- rnorm(1e06) 0.01s, Good: z <- x + y 6.23s, Bad: z <- numeric(1e06) for(i in 1:1e06) z[i] <- x[i] + y[i]
Allocate memory up front x <- rnorm(1e06); y <- rnorm(1e06) 0.01s, Good: z <- x + y One Hour, Terrible: z <- NULL for(i in 1:1e06) z[i] <- x[i] + y[i]
Get to the compiled code faster Could you: • Use pmax() rather than ifelse() • Use tabulate() rather than table() • Use lapply(split()) rather than tapply() • etc...
Get to the compiled code faster x <- rnorm(1e07); y <- rnorm(1e07) 0.42s, Good: pmax(x, y) 3.09s, Bad: ifelse(x < y, y, x)
R can be unpredictable x <- rnorm(1e08); y <- rnorm(50); n <- 100000 0.14s, 0.11s, Good: sum(x)/length(x), for(i in 1:n) sum(y)/length(y) 0.28s, 0.98s, Not So Good: mean(x), for(i in 1:n) mean(y)
R can be unpredictable x <- rnorm(1e08); y <- rnorm(50); n <- 100000 0.28s, 0.98s, Not So Good: mean(x), for(i in 1:n) mean(y) 0.28s, 0.26s, Improves In Loop: mean.default(x), for(i in 1:n) mean.default(y) 0.28s, 0.06s, Improves A Lot In Loop: .Internal(mean(x)), for(i in 1:n) .Internal(mean(y))
R can be unpredictable x <- rnorm(50); y <- rnorm(50); n <- 100000 0.06s: for(i in 1:n) x + y 0.12s, Seriously!: for(i in 1:n) (((((x + y)))))
Profiling R Code • Very simple do to and always useful • Often surprising (R can be unpredictable) • Indicates where to concentrate your optimization efforts • Large potential gains for virtually no effort • A small piece of code often takes up virtually all the running time
Profiling R Code Rprof() ## run some code here e.g. BadIdea(100000) Rprof(NULL) summaryRprof()
This function is stupid BadIdea <- function(n) { x <- seq(-1, 1, length=n) y <- numeric(n) for (i in 1:n) y[i] <-((det(as.matrix(x[i]))*2)^1.1)/n y }
Profiling R Code $by.self self.time self.pct total.time total.pct as.matrix 1.14 20.00 1.96 34.39 determinant 0.96 16.84 3.78 66.32 determinant.matrix 0.76 13.33 0.86 15.09 det 0.72 12.63 5.16 90.53 $ 0.66 11.58 0.66 11.58 array 0.62 10.88 0.70 12.28 BadIdea 0.44 7.72 5.70 100.00 as.matrix.default 0.12 2.11 0.82 14.39 ^ 0.10 1.75 0.10 1.75 as.vector 0.08 1.40 0.08 1.40 ncol 0.06 1.05 0.06 1.05 nrow 0.04 0.70 0.04 0.70
Profiling R Code $by.total total.time total.pct self.time self.pct BadIdea 5.70 100.00 0.44 7.72 det 5.16 90.53 0.72 12.63 determinant 3.78 66.32 0.96 16.84 as.matrix 1.96 34.39 1.14 20.00 determinant.matrix 0.86 15.09 0.76 13.33 as.matrix.default 0.82 14.39 0.12 2.11 array 0.70 12.28 0.62 10.88 $ 0.66 11.58 0.66 11.58 ^ 0.10 1.75 0.10 1.75 as.vector 0.08 1.40 0.08 1.40 ncol 0.06 1.05 0.06 1.05 nrow 0.04 0.70 0.04 0.70
Parallel Programming Multicore: single machine, not windows Snow: multiple machines, any OS Parallel: snow plus multicore mclapply(x, FUN, ..., mc.cores) parLapply(cl, x, FUN) Foreach: front-end for easier coding
Parallel Programming fn <- function(a) a * 1:10 lapply(c(22,56,70,43), fn) mclapply(c(22,56,70,43), fn, mc.cores = 4) cl <- makeCluster(4) parLapply(cl, c(22,56,70,43), fn) stopCluster(cl)
Parallel Programming Parallel random forest function (multicore type): parRandomForest<- function(xx, ..., ntree = 500, mc = 1) { rfwrap<- function(ntree, xx, ...) randomForest(x=xx, ntree=ntree, ...) rfpar<- mclapply(rep(ceiling(ntree/mc), mc), rfwrap, xx=xx, ..., mc.cores = mc) do.call(combine, rfpar) # not very fast }
Amazon EC2 • Like Hiring One or More Powerful Computers By The Hour • Start Amazon EC2 Instance(s) • AMIs available with R and R Studio Server pre-installed • $1.8 per hour (or part hour) for quadruple extra large high memory instance with linux 64-bit OS: 68.4 GB of memory 26 EC2 Compute Units (8 virtual cores)
Important Talk Clarification: When I answered ‘it’s important to turn it off’ I should have said ‘it’s important to turn off the instance explicitly by terminating (or stopping) it’. If you only turn off your computer, the EC2 instance will continue to run and you will continue to be charged by the hour. Thanks for attending, Alec.