Skip to content

Commit ba8ea8a

Browse files
authored
Remove very expensive unwind_protect() in string proxy assignment and push back (#378)
* Remove very expensive `unwind_protect()` in string proxy assignment and push back * NEWS bullet
1 parent dd78834 commit ba8ea8a

File tree

5 files changed

+69
-2
lines changed

5 files changed

+69
-2
lines changed

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# cpp11 (development version)
22

3+
* Repeated assignment to a `cpp11::writable::strings` vector through either
4+
`x[i] = elt` or `x.push_back(elt)` is now more performant, at the tradeoff
5+
of slightly less safety (as long as `elt` is actually a `CHARSXP` and `i` is
6+
within bounds, there is no chance of failure, which are the same kind of
7+
invariants placed on the other vector types) (#378).
8+
39
* Read only `r_vector`s now have a move constructor and move assignment
410
operator (#365).
511

cpp11test/R/cpp11.R

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ cpp11_safe_ <- function(x_sxp) {
160160
.Call(`_cpp11test_cpp11_safe_`, x_sxp)
161161
}
162162

163+
string_proxy_assignment_ <- function() {
164+
.Call(`_cpp11test_string_proxy_assignment_`)
165+
}
166+
167+
string_push_back_ <- function() {
168+
.Call(`_cpp11test_string_push_back_`)
169+
}
170+
163171
sum_dbl_for_ <- function(x) {
164172
.Call(`_cpp11test_sum_dbl_for_`, x)
165173
}

cpp11test/src/cpp11.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,20 @@ extern "C" SEXP _cpp11test_cpp11_safe_(SEXP x_sxp) {
310310
return cpp11::as_sexp(cpp11_safe_(cpp11::as_cpp<cpp11::decay_t<SEXP>>(x_sxp)));
311311
END_CPP11
312312
}
313+
// strings.cpp
314+
cpp11::writable::strings string_proxy_assignment_();
315+
extern "C" SEXP _cpp11test_string_proxy_assignment_() {
316+
BEGIN_CPP11
317+
return cpp11::as_sexp(string_proxy_assignment_());
318+
END_CPP11
319+
}
320+
// strings.cpp
321+
cpp11::writable::strings string_push_back_();
322+
extern "C" SEXP _cpp11test_string_push_back_() {
323+
BEGIN_CPP11
324+
return cpp11::as_sexp(string_push_back_());
325+
END_CPP11
326+
}
313327
// sum.cpp
314328
double sum_dbl_for_(cpp11::doubles x);
315329
extern "C" SEXP _cpp11test_sum_dbl_for_(SEXP x) {
@@ -504,6 +518,8 @@ static const R_CallMethodDef CallEntries[] = {
504518
{"_cpp11test_rcpp_sum_int_for_", (DL_FUNC) &_cpp11test_rcpp_sum_int_for_, 1},
505519
{"_cpp11test_remove_altrep", (DL_FUNC) &_cpp11test_remove_altrep, 1},
506520
{"_cpp11test_row_sums", (DL_FUNC) &_cpp11test_row_sums, 1},
521+
{"_cpp11test_string_proxy_assignment_", (DL_FUNC) &_cpp11test_string_proxy_assignment_, 0},
522+
{"_cpp11test_string_push_back_", (DL_FUNC) &_cpp11test_string_push_back_, 0},
507523
{"_cpp11test_sum_dbl_accumulate2_", (DL_FUNC) &_cpp11test_sum_dbl_accumulate2_, 1},
508524
{"_cpp11test_sum_dbl_accumulate_", (DL_FUNC) &_cpp11test_sum_dbl_accumulate_, 1},
509525
{"_cpp11test_sum_dbl_for2_", (DL_FUNC) &_cpp11test_sum_dbl_for2_, 1},

cpp11test/src/strings.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "cpp11/strings.hpp"
2+
3+
// Test benchmark for string proxy assignment performance.
4+
// We don't unwind_protect() before each `SET_STRING_ELT()` call,
5+
// as that kills performance.
6+
[[cpp11::register]] cpp11::writable::strings string_proxy_assignment_() {
7+
R_xlen_t size = 100000;
8+
9+
cpp11::writable::strings x(size);
10+
11+
cpp11::r_string elt(NA_STRING);
12+
13+
for (R_xlen_t i = 0; i < size; ++i) {
14+
x[i] = elt;
15+
}
16+
17+
return x;
18+
}
19+
20+
// Test benchmark for string push back performance.
21+
// We don't unwind_protect() before each `SET_STRING_ELT()` call,
22+
// as that kills performance.
23+
[[cpp11::register]] cpp11::writable::strings string_push_back_() {
24+
R_xlen_t size = 100000;
25+
26+
cpp11::writable::strings x;
27+
28+
cpp11::r_string elt(NA_STRING);
29+
30+
for (R_xlen_t i = 0; i < size; ++i) {
31+
x.push_back(elt);
32+
}
33+
34+
return x;
35+
}

inst/include/cpp11/strings.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ inline void r_vector<r_string>::set_elt(
6767
template <>
6868
inline typename r_vector<r_string>::proxy& r_vector<r_string>::proxy::operator=(
6969
const r_string& rhs) {
70-
unwind_protect([&] { SET_STRING_ELT(data_, index_, rhs); });
70+
// NOPROTECT: likely too costly to unwind protect every elt
71+
SET_STRING_ELT(data_, index_, rhs);
7172
return *this;
7273
}
7374

@@ -169,7 +170,8 @@ inline void r_vector<r_string>::push_back(r_string value) {
169170
while (length_ >= capacity_) {
170171
reserve(capacity_ == 0 ? 1 : capacity_ *= 2);
171172
}
172-
unwind_protect([&] { SET_STRING_ELT(data_, length_, value); });
173+
// NOPROTECT: likely too costly to unwind protect every elt
174+
SET_STRING_ELT(data_, length_, value);
173175
++length_;
174176
}
175177

0 commit comments

Comments
 (0)