2 min read

C++ integration with Rcpp

Benchmarking a computationally demanding function in R and C++

The Fibonacci sequence mathematically defined as \[F_n = \begin{cases} n, & n\le 2\\ F_{n-1} + F_{n-2}, & n>2 \end{cases}\]

R implementation

f <- function(n){
  if (n < 2) return(n)
  return(f(n - 1)+f(n - 2))
}

sapply(1:10, f)
##  [1]  1  1  2  3  5  8 13 21 34 55
Rscores <- benchmark(f(10),f(15), f(20), f(25), f(30))[,1:4]

Rscores
##    test replications elapsed relative
## 1 f(10)          100    0.00       NA
## 2 f(15)          100    0.08       NA
## 3 f(20)          100    0.76       NA
## 4 f(25)          100    8.58       NA
## 5 f(30)          100   95.69       NA
p <- Rscores %>% 
  filter(elapsed>0) %>% 
  ggplot(aes(test,elapsed, group = 1))+
  geom_line()+theme_minimal()+
  labs(title = "Fibonacci function benchmarked in R",
       y = "time elapsed (seconds)",
       x = "Function and iteration") 
print(p)

C++ implementation

The function defined above can be written within C++ as shown below and is callable from R through the Rcpp package. Rcpp::sourceCpp() is an option to source C++ files directly. Alternatively, the C++ function can be written within its own chunk using the Rcpp engine by using ```{Rcpp} as a code chunk header.

# Rcpp::sourceCpp("g.cpp")
#include <Rcpp.h>
//[[Rcpp::export]]
int g(int n) {
  if (n < 2) return(n);
  return(g(n-1) + g(n-2));
}
sapply(0:10, g)
##  [1]  0  1  1  2  3  5  8 13 21 34 55
Cscores <- benchmark(g(10),g(15), g(20), g(25),g(30))[,1:4]

Cscores
##    test replications elapsed relative
## 1 g(10)          100    0.00       NA
## 2 g(15)          100    0.00       NA
## 3 g(20)          100    0.00       NA
## 4 g(25)          100    0.03       NA
## 5 g(30)          100    0.22       NA
p <- Cscores %>% 
  ggplot(aes(test,elapsed, group = 1))+
  geom_line()+ theme_minimal()+
  labs(title = "Fibonacci function benchmarked in C++",
       y = "time elapsed (seconds)",
       x = "Function and iteration")

print(p)

Conclusion

The speed in calculating the 30th iteration of the Fibonacci sequence within C++ (0.22 seconds) massively outperforms R (95.69 seconds).

Pushing C++ further

Cscores <- benchmark(g(10),g(15), g(20), g(25),g(30),g(35),g(40))[,1:4]

Cscores %>% 
  ggplot(aes(test,elapsed))+
  geom_point()+
  labs(title = "Pushing C++ further")+
  theme_minimal()

The function \(f(35)\) takes around 35 minutes to complete in R. C++ can handle \(g(40)\) in just 26 seconds. test5