speed up bounds-checked element access and cache vector lengths - #1511
Open
kevinushey wants to merge 2 commits into
Open
kevinushey wants to merge 2 commits into
kevinushey wants to merge 2 commits into
Conversation
Enchufa2
reviewed
Sep 30, 2026
| @@ -55,7 +66,7 @@ namespace traits{ | |||
| void check_index(R_xlen_t i) const { | |||
| #ifndef RCPP_NO_BOUNDS_CHECK | |||
| if (i >= size) { | |||
Member
There was a problem hiding this comment.
Maybe the likely/unlikely macro technique used in the kernel helps too in these cases?
Member
There was a problem hiding this comment.
I see there are C++ attributes too, but that means C++20.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1510.
The per-element bounds check added in #1310 was noticeably slowing down element access in tight loops, including sugar expressions. This PR keeps the check (and its warning) but makes it cheap:
inst/include/Rcpp/vector/traits.h: the warning now lives in anoinline/coldhelper (warn_index_out_of_bounds()), so tinyformat and the 8 KB message buffer are no longer inlined into every loop body that reads an element.proxy_cache(used byCharacterVector,List,ExpressionVector) now caches the vector length inupdate()rather than callingp->size()on every access.inst/include/Rcpp/vector/Vector.h:size()/length()return the length cached byupdate()instead of callingRf_xlength()(an opaque call into libR) each time.update()already runs on everyset__(), so the cache is refreshed whenever the underlying SEXP changes.inst/include/Rcpp/vector/Matrix.h,SubMatrix.h: cache the number of columns alongside the existingnrows, soncol()/cols()(andMatrixRow::size()) no longer callRf_isMatrix()+Rf_getAttrib()each time.Benchmarks
R 4.6.1, arm64 macOS; the same functions compiled against
mastervs this branch. Results are identical between the two.x + y(1e7)x * y + 2 * x - y(1e7)for (i < x.size()) s += x[i](1e7)CharacterVectorloop,x[i] == NA_STRING(1e7)Listloop,Rf_isNull(x[i])(1e6)j < m.ncol()(1e6)MatrixRowloop withj < r.size()(1e6)Simple sugar expressions like
x + yare still slower than withRCPP_NO_BOUNDS_CHECK, since the (never-taken) branch still blocks vectorization. Letting sugar read plain vector operands without a per-element check would close that gap, but that's a larger change to the sugar machinery, so I've left it for a follow-up.Notes
Matrix'sdimattribute) were changed in place behind Rcpp's back, e.g. viaSETLENGTH()/R_resizeVector(). Nothing in Rcpp does this, andMatrix::nrow()already had the same caching behavior.RunAllRcppTests=yes(1624 tests, 0 failures).