Skip to content
This repository was archived by the owner on Dec 22, 2024. It is now read-only.

Commit d242a4c

Browse files
author
Jose Storopoli
committed
Atualizações Dirk de Rcpp Sugar.
1 parent 56f9ec3 commit d242a4c

File tree

9 files changed

+397
-367
lines changed

9 files changed

+397
-367
lines changed

1-Porque_CPP.Rmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ A figura \@ref(fig:cppfunction) mostra a anatomia de uma função em C++. Ela é
226226
* **Tipo de Retorno**: toda função em C++ deve especificar, antes do seu nome, o tipo de dado que é retornado pela função.
227227
* **Valor de Retorno**: toda função em C++ deve especificar explicitamente qual valor retornar com um `return`. No R podemos ser mais desleixados pois o valor de retorno será sempre a ultima declaração da função. Em C++ isto não funciona. A lógica de C++ é que a função termina quando ela atinge o primeiro `return` e retorna o dado/variável especificado(a).
228228

229-
```{r cppfunction, echo=FALSE, fig.cap='Anatomia de uma função em C++'}
229+
```{r cppfunction, echo=FALSE, fig.cap='Anatomia de uma função em C++. Figura adaptada da Vinheta Oficial Introdutória do `{Rcpp}` de Dirk Eddelbuettel'}
230230
knitr::include_graphics("images/cpp_function.png")
231231
```
232232

2-Rcpp.Rmd

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,10 @@ DataFrame df = DataFrame::create(v1, v2);
341341
DataFrame df = DataFrame::create( Named("V1") = v1 , _["V2"] = v2 );
342342
```
343343

344+
## `{Rcpp}` Sugar
345+
346+
Além dos tipos de dados, `{Rcpp}` também tem uma ampla gama de "açúcares" sintáticos (*syntactic sugar*) para as mais variadas operações e funções. Antes de tentar criar algo do zero veja se não há um [`{Rccp}` Sugar para isso já implementado na vinheta](http://dirk.eddelbuettel.com/code/rcpp/Rcpp-sugar.pdf).
347+
344348
## Exemplo -- Multiplicação de Matrizes
345349

346350
Está na hora de colocarmos o que vimos em prática. Nesse caso vamos comparar multiplicar duas matrizes usando o R (operador `%*%`) e usando o C++ com `{Rcpp}`.
@@ -387,15 +391,14 @@ Aqui eu vou ser um pouco abusado no exemplo e já vou pular direto para um algor
387391

388392
Soma dos quadrados é algo que ocorre bastante em computação científica, especialmente quando estamos falando de regressão, mínimos quadrados, ANOVA etc. Vamos fazer três implementações de uma função que aceita como parâmetro um vetor de números reais (C++ `double` / R `numeric`) e computa a soma de todos os elementos do vetor elevados ao quadrado:
389393

390-
1. `sum_of_squares_R()`: feita no R com vetorização usando o `{purrr}`.
394+
1. `sum_of_squares_R()`: feita no R com vetorização.
391395
2. `sum_of_squares_rcpp()`: feita de maneira ingênua no C++ com dois loops `for` triviais que (1) multiplica cada elemento consigo mesmo e (2) adiciona todos os elementos do vetor à uma variável `double`.
392-
3. `sum_of_squares_cpp20()`: solução elegante que usa `transform_reduce` para transformar todos os elementos multiplicando-os por si mesmos e ao mesmo tempo somando todos os elementos.
396+
3. `sum_of_squares_rcpp20()`: solução elegante que usa `transform_reduce` para transformar todos os elementos multiplicando-os por si mesmos e ao mesmo tempo somando todos os elementos.
397+
4. `sum_of_squares_rcpp_sugar()`: solução usando [`{Rcpp}` Sugar](http://dirk.eddelbuettel.com/code/rcpp/Rcpp-sugar.pdf).
393398

394399
```{r sum_of_squares_R}
395-
library(magrittr)
396400
sum_of_squares_R <- function(v) {
397-
purrr::map_dbl(v, ~ .x * .x) %>%
398-
purrr::reduce(`+`)
401+
sum(v * v)
399402
}
400403
```
401404

@@ -423,14 +426,19 @@ double sum_of_squares_rcpp(NumericVector v){
423426
}
424427
425428
// [[Rcpp::export]]
426-
double sum_of_squares_cpp20(const NumericVector v){
429+
double sum_of_squares_rcpp20(const NumericVector v){
427430
428431
return transform_reduce(v.cbegin(),
429432
v.cend(),
430433
0L,
431434
std::plus{},
432435
[] (auto i) {return i * i;});
433436
}
437+
438+
// [[Rcpp::export]]
439+
double sum_of_squares_rcpp_sugar(NumericVector v){
440+
return(sum(v*v));
441+
}
434442
```
435443

436444
```{r bench_sum_of_squares}
@@ -440,13 +448,14 @@ v <- rnorm(n)
440448
bench::mark(
441449
R = sum_of_squares_R(v),
442450
rcpp = sum_of_squares_rcpp(v),
443-
cpp20 = sum_of_squares_cpp20(v),
451+
rcpp20 = sum_of_squares_rcpp20(v),
452+
rcppsugar = sum_of_squares_rcpp_sugar(v),
444453
check = FALSE,
445454
time_unit = "us"
446455
)
447456
```
448457

449-
Mais um sucesso! Ganho de 600x `r emo::ji("exploding_head")` para um vetor com `r format(n, big.mark = ".", decimal.mark = ",")` elementos! O mais impressionante é que uma solução simples e ingênua com dois loops `for` em C++ ficou quase tão rápida quanto uma solução C++ usando `transform_reduce()`!
458+
Aqui vemos como a vetorização do R funciona muito bem. É mais rápida que quase todas as implementações em `{Rcpp}`, exceto quando usamos o [`{Rcpp}` Sugar](http://dirk.eddelbuettel.com/code/rcpp/Rcpp-sugar.pdf) já que temos um ganho de 2x para um vetor com `r format(n, big.mark = ".", decimal.mark = ",")` elementos.
450459

451460
## `{Rcpp}` e Boost
452461

@@ -555,6 +564,7 @@ Diversos materiais me ajudaram a aprender e criar esse conjunto de tutoriais de
555564
* Livro [Seamless R and C++ Integration with Rcpp](http://www.rcpp.org/book/) do criador do ecossitema `{Rcpp}` Dirk Eddelbuettel.
556565
* [Capítulo 25 -- Rewriting R code in C++](https://adv-r.hadley.nz/rcpp.html) do livro Advanced R do Hadley Wickham.
557566
* [Galeria de exemplos de `{Rcpp}`](https://gallery.rcpp.org).
567+
* [Vinheta do `{Rcpp}` Sugar](http://dirk.eddelbuettel.com/code/rcpp/Rcpp-sugar.pdf).
558568
* Livro [Rcpp for Everyone](https://teuder.github.io/rcpp4everyone_en/) do Masaki E. Tsuda.
559569
* [Documentação Não-Oficial](https://thecoatlessprofessor.com/programming/cpp/unofficial-rcpp-api-documentation/) de `{Rcpp}` do James Balamuta (também conhecido como TheCoatlessProfessor).
560570
* Vídeo do [Dirk Eddelbuettel na conferência useR! 2020 sobre `{Rcpp}`](https://youtu.be/57H34Njrns4).

5-cpp11.Rmd

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -133,50 +133,54 @@ tibble::tribble(
133133

134134
Vamos reutilizar o exemplo `sum_of_squares` do [tutorial 2. Como incorporar C++ no R - {Rcpp}](2-Rcpp.html).
135135

136-
Soma dos quadrados é algo que ocorre bastante em computação científica, especialmente quando estamos falando de regressão, mínimos quadrados, ANOVA etc. Vamos comparar a função usando dois loops `for`^[`{cpp11}` ainda não possui suporte à funcionalidades de C++20 -- `std::transform_reduce()`.] tanto no `{Rcpp}` quanto no `{cpp11}`. Lembrando que esta implementação será uma função que aceita como parâmetro um vetor de números reais (C++ `double` / R `numeric`) e computa a soma de todos os elementos do vetor elevados ao quadrado.
136+
Soma dos quadrados é algo que ocorre bastante em computação científica, especialmente quando estamos falando de regressão, mínimos quadrados, ANOVA etc. Vamos comparar a função usando um [`std::acummulate`](https://en.cppreference.com/w/cpp/algorithm/accumulate) de C+11 STL^[`{cpp11}` ainda não possui suporte à funcionalidades de C++20 -- `std::transform_reduce()`.] tanto no `{Rcpp}` quanto no `{cpp11}`. Lembrando que esta implementação será uma função que aceita como parâmetro um vetor de números reais (C++ `double` / R `numeric`) e computa a soma de todos os elementos do vetor elevados ao quadrado.
137+
138+
Para ser uma comparação justa, vamos usar também do [tutorial 2. Como incorporar C++ no R - {Rcpp}](2-Rcpp.html), a função `sum_of_squares_rcpp_sugar()` que usa [`{Rcpp}` Sugar](http://dirk.eddelbuettel.com/code/rcpp/Rcpp-sugar.pdf).
137139

138140
```{Rcpp sum_of_squares_rcpp}
139141
#include <Rcpp.h>
140-
// [[Rcpp::plugins("cpp11")]]
142+
#include <numeric>
141143
142144
using namespace Rcpp;
143145
146+
// [[Rcpp::plugins("cpp11")]]
147+
144148
// [[Rcpp::export]]
145149
double sum_of_squares_rcpp(NumericVector v){
146150
double sum_of_elems = 0;
147151
148-
// primeiro for multiplicando cada elemento consigo mesmo
149-
for(int i=0; i < v.size(); i++){
150-
v[i] = v[i] * v[i];
151-
}
152-
153-
// segundo for somando todos os elementos
154-
for(auto it = v.cbegin(); it != v.cend(); ++it)
155-
sum_of_elems += *it;
152+
sum_of_elems += std::accumulate(v.cbegin(),
153+
v.cend(),
154+
0.0,
155+
[] (double i, double j) {return i + (j * j);});
156156
return sum_of_elems;
157157
}
158+
159+
// [[Rcpp::export]]
160+
double sum_of_squares_rcpp_sugar(NumericVector v){
161+
return(sum(v*v));
162+
}
158163
```
159164

160165

161166
```{cpp11 sum_of_squares_cpp11}
162167
#include "cpp11/doubles.hpp" // aqui usando somente o header doubles
168+
#include <numeric>
169+
163170
using namespace cpp11;
164171
namespace writable = cpp11::writable;
165172
166173
167-
[[cpp11::register]] double sum_of_squares_cpp11(writable::doubles v){
174+
[[cpp11::register]] double sum_of_squares_cpp11(doubles v){
168175
double sum_of_elems = 0;
169176
170-
// primeiro for multiplicando cada elemento consigo mesmo
171-
for(int i=0; i < v.size(); i++){
172-
v[i] = v[i] * v[i];
173-
}
174-
175-
// segundo for somando todos os elementos
176-
for(auto it = v.cbegin(); it != v.cend(); ++it)
177-
sum_of_elems += *it;
177+
sum_of_elems += std::accumulate(v.cbegin(),
178+
v.cend(),
179+
0.0,
180+
[] (double i, double j) {return i + (j * j);});
178181
return sum_of_elems;
179182
}
183+
180184
```
181185

182186
```{r bench-sum_of_squares}
@@ -186,11 +190,12 @@ v <- rnorm(n)
186190
bench::mark(
187191
Rcpp = sum_of_squares_rcpp(v),
188192
cpp11 = sum_of_squares_cpp11(v),
193+
Rcppsugar = sum_of_squares_rcpp_sugar(v),
189194
check = FALSE,
190195
)
191196
```
192197

193-
Apesar de ser uma nova proposta o `{cpp11}`, pelo menos neste simples benchmark e no meu computador, possui um desempenho bem inferior ao do `{Rcpp}`: 60x mais lerdo...
198+
Quando usada a biblioteca padrão C++11 STL tanto `{cpp11}` quanto `{Rcpp}` e `{Rcpp}` Sugar, pelo menos neste simples benchmark e no meu computador, possuem um desempenho similar para um vetor com `r format(n, big.mark = ".", decimal.mark = ",")` elementos. É claro que solução do `{Rcpp}` Sugar é muito mais elegante e simples com uma única linhana função.
194199

195200
## `{cpp11}` e Rmarkdown
196201

0 commit comments

Comments
 (0)