Multithread the order-2 likelihood kernels on the RCPP backend#10
Merged
Conversation
likelihoodGP2 and likelihoodGP2cov now take a cores argument and
parallelize via std::thread (no OpenMP, no new dependencies): the
no-covariate path builds the distinct-(y,z) g-vector cache concurrently
over read-only tables, the covariate path computes per-t log-terms with
thread-private tables into a preallocated vector. Accumulation stays
serial in the original order, so results are bit-identical for any
thread count (verified against a pre-change build: NLL values, fitted
parameters, and log-likelihoods all match exactly).
cocoReg's previously dead cores argument is now live and flows down to
the kernels; it defaults to physical cores - 1 (mirroring the
JULIA_NUM_THREADS convention) and is capped at 2 while
_R_CHECK_LIMIT_CORES_ is set so CRAN checks stay policy-compliant.
Worker threads never touch the R API; the only stop() condition is
checked on the main thread before spawning. Order-1 kernels are
microsecond-scale and stay serial. src/Makevars{,.win} add -pthread.
Measured on 14 physical cores (T = 1000, GP2): full cocoReg fits drop
from 6.3 s to 1.1 s (no covariates) and from 11.4 s to 1.5 s (with
covariates); single NLL calls scale 6.4x / 8.4x at 13 threads.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
The RCPP backend was entirely single-threaded, and
cocoReg'scoresargument was dead — nothing read it. This PR parallelizes the two kernels that dominate order-2 fit time,likelihoodGP2andlikelihoodGP2cov, using C++11std::thread(no OpenMP, no new dependencies), and makescoreslive.likelihoodGP2: the per-(y,z)g-vector cache — rebuilt every optimizer iteration, the dominant cost — is now built concurrently across the distinct pairs, over read-only lookup tables ensured up front.likelihoodGP2cov: each observation's contribution is independent (lambda_tvaries, nothing is cacheable), so per-tlog-terms are computed concurrently with thread-private tables into a preallocated vector.identical()on NLL values at 1 and 14 threads, plus fitted parameters and log-likelihoods of full fits).eta = 0) and benefit equally. Order-1 kernels are microsecond-scale, where thread-spawn overhead would exceed any gain — they stay serial.cocoReg(cores=)defaults to physical cores − 1 (mirroring theJULIA_NUM_THREADSconvention from Default JULIA_NUM_THREADS to half the physical cores for the Julia backend #9), capped at 2 while_R_CHECK_LIMIT_CORES_is set so CRAN checks stay policy-compliant.stop()condition is checked on the main thread before spawning, and worker exceptions are re-raised as an R error after joining.src/Makevars/Makevars.winadd-pthreadfor the Linux/Windows toolchains.Benchmarks
Measured on 14 physical cores (Apple Silicon), GP2 with T = 1000:
likelihoodGP2(per call)likelihoodGP2cov(per call)cocoRegGP2 fitcocoRegGP2cov fitTest plan
identical()atcores = 1andcores = 14— all exact matches.devtools::test(): FAIL 0 / WARN 29 / PASS 18 — identical counts to the pre-change baseline.R CMD check: 0 errors, 0 warnings, 1 environment-only NOTE (future file timestamps).🤖 Generated with Claude Code