Skip to content

Commit 1b5d31c

Browse files
committed
update gha
1 parent 2cf1438 commit 1b5d31c

File tree

6 files changed

+79
-32
lines changed

6 files changed

+79
-32
lines changed

.Rbuildignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@
2121
^scripts$
2222
^vignettes$
2323
^cpp11headers$
24-
.*\.md$
24+
^.*\.md$
25+
!^tests/testthat/_snaps/.*\.md$

.github/workflows/R-CMD-check.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
env:
4545
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
4646
R_KEEP_PKG_SOURCE: yes
47+
TESTTHAT_PARALLEL: false
4748

4849
steps:
4950
- uses: actions/checkout@v4

inst/include/cpp4r/cpp_version.hpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,21 @@
147147
#else
148148
#define CPP4R_HOT
149149
#define CPP4R_COLD
150+
#endif
151+
152+
// CPP4R_ASSUME_ALIGNED: hint to compiler about pointer alignment
153+
// Useful for SIMD optimization when working with R vector data
154+
#if defined(__GNUC__) || defined(__clang__)
155+
#define CPP4R_ASSUME_ALIGNED(ptr, alignment) __builtin_assume_aligned(ptr, alignment)
156+
#else
157+
#define CPP4R_ASSUME_ALIGNED(ptr, alignment) (ptr)
158+
#endif
159+
160+
// CPP4R_PREFETCH: prefetch data to cache for better performance
161+
#if defined(__GNUC__) || defined(__clang__)
162+
#define CPP4R_PREFETCH(addr) __builtin_prefetch(addr)
163+
#define CPP4R_PREFETCH_WRITE(addr) __builtin_prefetch(addr, 1)
164+
#else
165+
#define CPP4R_PREFETCH(addr) ((void)0)
166+
#define CPP4R_PREFETCH_WRITE(addr) ((void)0)
150167
#endif

inst/include/cpp4r/doubles.hpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,29 @@ inline doubles as_doubles(SEXP x) {
8383
size_t len = xn.size();
8484
writable::doubles ret(len);
8585

86-
std::transform(xn.begin(), xn.end(), ret.begin(), [](int value) {
87-
return value == NA_INTEGER ? NA_REAL : static_cast<double>(value);
88-
});
86+
const int* CPP4R_RESTRICT x_ptr = INTEGER(xn.data());
87+
double* CPP4R_RESTRICT ret_ptr = REAL(ret.data());
88+
89+
// Optimized loop with branch prediction hints
90+
for (size_t i = 0; i < len; ++i) {
91+
int val = x_ptr[i];
92+
ret_ptr[i] = CPP4R_LIKELY(val != NA_INTEGER) ? static_cast<double>(val) : NA_REAL;
93+
}
8994

9095
return ret;
9196
} else if (detail::r_typeof(x) == LGLSXP) {
9297
logicals xn(x);
9398
size_t len = xn.size();
9499
writable::doubles ret(len);
95100

96-
std::transform(xn.begin(), xn.end(), ret.begin(), [](bool value) {
97-
return value == NA_LOGICAL ? NA_REAL : static_cast<double>(value);
98-
});
101+
const int* CPP4R_RESTRICT x_ptr = LOGICAL(xn.data());
102+
double* CPP4R_RESTRICT ret_ptr = REAL(ret.data());
103+
104+
// Optimized loop with branch prediction hints
105+
for (size_t i = 0; i < len; ++i) {
106+
int val = x_ptr[i];
107+
ret_ptr[i] = CPP4R_LIKELY(val != NA_LOGICAL) ? static_cast<double>(val) : NA_REAL;
108+
}
99109

100110
return ret;
101111
}

inst/include/cpp4r/matrix.hpp

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,12 @@ class matrix : public matrix_slices<S> {
175175

176176
matrix(int nrow, int ncol)
177177
: matrix_slices<S>(nrow, ncol), vector_(R_xlen_t(nrow * ncol)) {
178-
vector_.attr(R_DimSymbol) = {nrow, ncol};
178+
// Fast path: Set dimensions directly using R API without intermediate protection
179+
SEXP dims = PROTECT(Rf_allocVector(INTSXP, 2));
180+
INTEGER(dims)[0] = nrow;
181+
INTEGER(dims)[1] = ncol;
182+
Rf_setAttrib(vector_.data(), R_DimSymbol, dims);
183+
UNPROTECT(1);
179184
}
180185

181186
using matrix_slices<S>::nrow;
@@ -241,6 +246,15 @@ class matrix : public matrix_slices<S> {
241246

242247
r_vector<r_string> names() const { return r_vector<r_string>(vector_.names()); }
243248

249+
// Fast-path accessors for high-performance operations
250+
CPP4R_ALWAYS_INLINE const double* CPP4R_RESTRICT data_ptr() const {
251+
return REAL(vector_.data());
252+
}
253+
254+
CPP4R_ALWAYS_INLINE double* CPP4R_RESTRICT data_ptr_writable() {
255+
return REAL(vector_.data());
256+
}
257+
244258
CPP4R_ALWAYS_INLINE T operator()(int row, int col) const {
245259
return vector_[row + (col * nrow())];
246260
}
@@ -291,17 +305,18 @@ inline doubles_matrix<S> as_doubles_matrix(SEXP x) {
291305
R_xlen_t size = static_cast<R_xlen_t>(nrow) * ncol;
292306
writable::doubles_matrix<S> ret(nrow, ncol);
293307

294-
const int* x_ptr = INTEGER(xn.data());
295-
double* ret_ptr = REAL(ret.data());
308+
const int* CPP4R_RESTRICT x_ptr = INTEGER(xn.data());
309+
double* CPP4R_RESTRICT ret_ptr = REAL(ret.data());
296310

297-
// Simple loop (compiler can auto-vectorize)
311+
// Optimized loop that compiler can auto-vectorize
298312
for (R_xlen_t i = 0; i < size; ++i) {
299-
ret_ptr[i] = (x_ptr[i] == NA_INTEGER) ? NA_REAL : static_cast<double>(x_ptr[i]);
313+
int val = x_ptr[i];
314+
ret_ptr[i] = CPP4R_LIKELY(val != NA_INTEGER) ? static_cast<double>(val) : NA_REAL;
300315
}
301316

302317
// Preserve attributes like dimnames
303318
SEXP dimnames = Rf_getAttrib(x, R_DimNamesSymbol);
304-
if (dimnames != R_NilValue) {
319+
if (CPP4R_UNLIKELY(dimnames != R_NilValue)) {
305320
Rf_setAttrib(ret.data(), R_DimNamesSymbol, dimnames);
306321
}
307322

@@ -314,17 +329,18 @@ inline doubles_matrix<S> as_doubles_matrix(SEXP x) {
314329
R_xlen_t size = static_cast<R_xlen_t>(nrow) * ncol;
315330
writable::doubles_matrix<S> ret(nrow, ncol);
316331

317-
const int* x_ptr = LOGICAL(xn.data());
318-
double* ret_ptr = REAL(ret.data());
332+
const int* CPP4R_RESTRICT x_ptr = LOGICAL(xn.data());
333+
double* CPP4R_RESTRICT ret_ptr = REAL(ret.data());
319334

320-
// Simple loop
335+
// Optimized loop
321336
for (R_xlen_t i = 0; i < size; ++i) {
322-
ret_ptr[i] = (x_ptr[i] == NA_LOGICAL) ? NA_REAL : static_cast<double>(x_ptr[i]);
337+
int val = x_ptr[i];
338+
ret_ptr[i] = CPP4R_LIKELY(val != NA_LOGICAL) ? static_cast<double>(val) : NA_REAL;
323339
}
324340

325341
// Preserve dimnames
326342
SEXP dimnames = Rf_getAttrib(x, R_DimNamesSymbol);
327-
if (dimnames != R_NilValue) {
343+
if (CPP4R_UNLIKELY(dimnames != R_NilValue)) {
328344
Rf_setAttrib(ret.data(), R_DimNamesSymbol, dimnames);
329345
}
330346

@@ -347,28 +363,29 @@ inline integers_matrix<S> as_integers_matrix(SEXP x) {
347363
int ncol = xn.ncol();
348364
R_xlen_t size = static_cast<R_xlen_t>(nrow) * ncol;
349365

350-
const double* x_ptr = REAL(xn.data());
366+
const double* CPP4R_RESTRICT x_ptr = REAL(xn.data());
351367

352368
// First pass: validate all values are integer-like
353-
// Note: Cannot easily parallelize validation with early exit
354369
for (R_xlen_t i = 0; i < size; ++i) {
355-
if (!ISNA(x_ptr[i]) && !is_convertible_without_loss_to_integer(x_ptr[i])) {
370+
double val = x_ptr[i];
371+
if (CPP4R_UNLIKELY(!ISNA(val) && !is_convertible_without_loss_to_integer(val))) {
356372
throw std::runtime_error("Cannot convert doubles matrix to integers: not all elements are integer-like");
357373
}
358374
}
359375

360376
// Second pass: convert
361377
writable::integers_matrix<S> ret(nrow, ncol);
362-
int* ret_ptr = INTEGER(ret.data());
378+
int* CPP4R_RESTRICT ret_ptr = INTEGER(ret.data());
363379

364-
// Simple loop
380+
// Optimized conversion loop
365381
for (R_xlen_t i = 0; i < size; ++i) {
366-
ret_ptr[i] = ISNA(x_ptr[i]) ? NA_INTEGER : static_cast<int>(x_ptr[i]);
382+
double val = x_ptr[i];
383+
ret_ptr[i] = CPP4R_LIKELY(!ISNA(val)) ? static_cast<int>(val) : NA_INTEGER;
367384
}
368385

369386
// Preserve dimnames
370387
SEXP dimnames = Rf_getAttrib(x, R_DimNamesSymbol);
371-
if (dimnames != R_NilValue) {
388+
if (CPP4R_UNLIKELY(dimnames != R_NilValue)) {
372389
Rf_setAttrib(ret.data(), R_DimNamesSymbol, dimnames);
373390
}
374391

@@ -380,17 +397,18 @@ inline integers_matrix<S> as_integers_matrix(SEXP x) {
380397
R_xlen_t size = static_cast<R_xlen_t>(nrow) * ncol;
381398
writable::integers_matrix<S> ret(nrow, ncol);
382399

383-
const int* x_ptr = LOGICAL(xn.data());
384-
int* ret_ptr = INTEGER(ret.data());
400+
const int* CPP4R_RESTRICT x_ptr = LOGICAL(xn.data());
401+
int* CPP4R_RESTRICT ret_ptr = INTEGER(ret.data());
385402

386-
// Simple loop
403+
// Optimized loop
387404
for (R_xlen_t i = 0; i < size; ++i) {
388-
ret_ptr[i] = (x_ptr[i] == NA_LOGICAL) ? NA_INTEGER : x_ptr[i];
405+
int val = x_ptr[i];
406+
ret_ptr[i] = CPP4R_LIKELY(val != NA_LOGICAL) ? val : NA_INTEGER;
389407
}
390408

391409
// Preserve dimnames
392410
SEXP dimnames = Rf_getAttrib(x, R_DimNamesSymbol);
393-
if (dimnames != R_NilValue) {
411+
if (CPP4R_UNLIKELY(dimnames != R_NilValue)) {
394412
Rf_setAttrib(ret.data(), R_DimNamesSymbol, dimnames);
395413
}
396414

inst/include/cpp4r/r_vector_impl.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ inline T r_vector<T>::operator[](const int pos) const {
133133
#endif
134134

135135
template <typename T>
136-
inline T r_vector<T>::operator[](const R_xlen_t pos) const {
136+
CPP4R_ALWAYS_INLINE T r_vector<T>::operator[](const R_xlen_t pos) const {
137137
// Handles ALTREP, VECSXP, and STRSXP cases through `get_elt()`
138-
const underlying_type elt = (data_p_ != nullptr) ? data_p_[pos] : get_elt(data_, pos);
138+
const underlying_type elt = CPP4R_LIKELY(data_p_ != nullptr) ? data_p_[pos] : get_elt(data_, pos);
139139
return static_cast<T>(elt);
140140
}
141141

0 commit comments

Comments
 (0)