You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
per-element bounds check and uncached lengths slow down vector access and sugar #1510
The bounds check added to vector element access in #1310 has a noticeable cost in tight loops, including sugar expressions (which read every operand through Vector::operator[]).
Two things contribute:
r_vector_cache::check_index() and proxy_cache::check_index() (inst/include/Rcpp/vector/traits.h) call Rcpp::warning() inline. That pulls tinyformat plus an 8 KB stack buffer into every loop body that reads an element, which appears to block inlining and vectorization even though the branch is never taken.
proxy_cache::check_index() calls p->size() on every access, and Vector::size() / length() call Rf_xlength() on every call. With current R headers this is an opaque function call into libR, so for (R_xlen_t i = 0; i < x.size(); i++) makes a libR call per iteration -- even though r_vector_cache already stores the length. Similarly, Matrix::ncol() calls Rf_isMatrix() + Rf_getAttrib() on each call (and MatrixRow::size() goes through it), while nrow() is already cached.
Micro-benchmarks (R 4.6.1, arm64 macOS, 1e7 elements, current master):
code
current
with RCPP_NO_BOUNDS_CHECK
reference
NumericVector z = x + y;
26.7 ms
2.8 ms
2.5 ms (base R x + y)
for (i < x.size()) s += x[i];
12.8 ms
--
5.4 ms (hoisted length, raw pointer)
CharacterVector NA count, x[i] == NA_STRING
41.2 ms
20.6 ms
13.0 ms (raw STRING_ELT)
row-major matrix loop with j < m.ncol() (1e6)
~17 ms
--
~1 ms (hoisted)
Proposed fix (no behavior change; the out-of-bounds warning is kept):
Move the warning into a separate noinline / cold helper and mark the check as unlikely.
Have Vector::size() return the length cached by update() (which already runs on every set__()), and cache the length in proxy_cache too.
Cache the number of columns in Matrix alongside nrows.
A possible follow-up would be letting sugar read plain-vector operands without a per-element check (as the old Fast extractor did), but that is a larger change to the sugar machinery.
Activity
speed up bounds-checked element access and cache vector lengths (#1511)