EDI v1.0.0 Exhaustive C++ Model Fit Benchmarks

Generated: 2026-08-05 17:35:36 IDT

R analog of benchmark_model_fits_python.html — same three tables, same table shape, same three-color row coding.

This report compares the performance of EDI's Rcpp-optimized model fitting paths against low-level canonical R implementations (e.g., glm.fit, lm.fit, coxph.fit) where possible.

Compilation Context

These rows are read from build metadata compiled into the loaded EDI shared object via edi_build_info_cpp().

Compilation warning: EDI model-fit timings are sensitive to the compiler flags used to build the loaded EDI.so. If EDI is compiled without the proper optimized flags, or with flags that are known to degrade these kernels such as problematic LTO builds, the benchmark can show substantial performance regressions that reflect the binary build rather than the modeling algorithms.

Benchmark Dataset Specification

All benchmarks were performed on a synthetic clinical-trial-scale dataset generated for each response type. The data generation process ensures numerical stability and fair solver comparison by using the following parameters:

Methodology

Results

EDI faster, significant   NA timing comparison   no canonical R implementation

ClassResponseEDI Time (ms)Canonical PkgCanonical FuncCanonical Time (ms)SpeedupTiming Pval
InferenceAllSimpleWilcoxcontinuous0.30statsHL median pairwise diff2.227.42x5.71e-17***
InferenceContinGLMM (pairs)continuous0.31Noneno canonical R implementationNANANA
InferenceContinOLScontinuous0.02statslm.fit0.105.45x2.54e-23***
InferenceContinRobustRegrcontinuous0.12MASSrlm(MM)61.75498.44x2.98e-34***
InferenceIncidBinomialIdentityRiskDiffincidence0.18statsglm.fit(ident)14.9181.64x1.01e-23***
InferenceIncidGCompRiskDiffincidence0.19statsglm.fit+gcomp(RD)1.849.77x8.94e-24***
InferenceIncidGCompRiskRatioincidence0.19statsglm.fit+gcomp(RR)1.859.71x3.14e-20***
InferenceIncidKKCondLogitGLMMOneLikincidence27.29Noneno canonical R implementationNANANA
InferenceIncidLogBinomialincidence2.60statsglm.fit(log)16.626.39x4.6e-30***
InferenceIncidLogRegrincidence0.16statsglm.fit1.9212.06x2.87e-23***
InferenceIncidModifiedPoissonincidence0.14statsglm.fit(modified)2.4917.62x2.89e-23***
InferenceIncidProbitRegrincidence0.41statsglm.fit(probit)2.476x2.25e-21***
InferenceIncidRiskDiffincidence0.02statslm.fit(LPM)0.187.71x3.96e-27***
InferenceCountGLMM (pairs)count11.25Noneno canonical R implementationNANANA
InferenceCountHurdleNegBincount2.23psclhurdle(nb)58.1226.03x5.41e-33***
InferenceCountHurdlePoissoncount1.97psclhurdle23.0011.67x1.87e-32***
InferenceCountHurdlePoisson (pairs)count6.34Noneno canonical R implementationNANANA
InferenceCountKKCondPoissonOneLikcount0.12Noneno canonical R implementationNANANA
InferenceCountNegBincount0.53MASSglm.nb64.50121.44x1.07e-33***
InferenceCountPoissoncount0.13statsglm.fit2.6821.27x6.28e-23***
InferenceCountQuasiPoissoncount0.15statsglm.fit(quasi)2.2314.52x4.79e-25***
InferenceCountRobustPoissoncount0.17statsglm.fit2.8016.9x1.1e-20***
InferenceCountZeroInflatedNegBincount1.43psclzeroinfl(nb)212.00148.73x1.3e-23***
InferenceCountZeroInflatedPoissoncount4.34psclzeroinfl85.1719.63x4.9e-24***
InferencePropBetaRegrproportion1.20betaregbetareg.fit39.2132.77x2.49e-30***
InferencePropFractionalLogitproportion0.16statsglm.fit(quasi)1.6710.2x9.95e-21***
InferencePropGCompMeanDiffproportion0.24statsglm.fit(quasi)+gcomp1.978.07x2.41e-22***
InferencePropZeroOneInflatedBetaRegrproportion7.71Noneno canonical R implementationNANANA
InferenceSurvivalCoxPHRegrsurvival0.41survivalcoxph.fit(breslow)0.681.68x9.57e-23***
InferenceSurvivalKKWeibullFrailtyOneLiksurvival3.74Noneno canonical R implementationNANANA
InferenceSurvivalKMDiffsurvival0.02survivalsurvfit(median)4.66223.63x2.07e-27***
InferenceSurvivalLogRanksurvival0.03survivalsurvdiff3.35118.09x7.7e-19***
InferenceSurvivalRestrictedMeanDiffsurvival0.02survivalsurvfit(rmean)3.88186.91x2.38e-29***
InferenceSurvivalStratCoxPHRegrsurvival0.82survivalcoxph.fit(strat)1.131.39x2.43e-07***
InferenceSurvivalWeibullRegrsurvival0.07survivalsurvreg5.0170.58x2.07e-23***
InferenceOrdinalAdjCatLogitRegrordinal0.60VGAMvglm(acat)18.1930.25x7.42e-19***
InferenceOrdinalCLMM (pairs)ordinal29.70Noneno canonical R implementationNANANA
InferenceOrdinalCauchitRegrordinal0.72ordinalclm(cauchit)14.4320.14x4.52e-28***
InferenceOrdinalCloglogRegrordinal0.59ordinalclm(cloglog)10.0817x1.54e-27***
InferenceOrdinalContRatioRegrordinal0.27VGAMvglm(cratio)17.2763.97x1.59e-29***
InferenceOrdinalGCompMeanDiffordinal0.60ordinalclm+gcomp16.2326.89x4.3e-13***
InferenceOrdinalGLMM (pairs)ordinal62.88Noneno canonical R implementationNANANA
InferenceOrdinalOrderedProbitRegrordinal0.52ordinalclm(probit)9.3717.95x1.83e-14***
InferenceOrdinalPropOddsRegrordinal0.63ordinalclm9.1014.46x3.59e-23***
InferenceOrdinalStereotypeLogitRegrordinal1.35Noneno canonical R implementationNANANA

Wald Test Performance (Full Inference)

This table compares the performance of Full Inference (Model Fit + Standard Error calculation + P-value derivation). Unlike the point-estimation table above, these results include the computational cost of the variance-covariance matrix (Hessian or Fisher Information) and the Wald test statistic calculation. All paths (EDI and Canonical) use a reduced sample size ($N=200$) for this full-inference benchmark to ensure iterative stability. Stratified Cox Exception: For InferenceSurvivalStratCoxPHRegr, the benchmark injects low-cardinality covariates before outcome generation so the row exercises a genuinely stratified Cox fit rather than the unstratified fallback. EDI regression models (Logistic, Poisson) are benchmarked using the IRLS optimizer for these Wald tests. Solver-Only Prebuilds: Benchmark setup prebuilds exposed observed-data design matrices, reduced design matrices, strata IDs, and other fixed working inputs outside the timed region when the implementation exposes those hooks. The timed region then measures the full-inference kernel on those fixed inputs. Limitation: Some canonical comparators only expose formula-based APIs rather than comparable low-level fit kernels. Those rows remain included, but their canonical timings may still contain formula/model-frame overhead beyond the numerical solver, variance, and p-value work itself. Timing Note: All timings are medians over 30 warmed runs measured with adaptive batched system.time; paths below 0.01 ms use microbenchmark(times = 5000) instead. Timing P-Value: Timing Pval reports a Welch two-sample t-test comparing the EDI and canonical timing replicate distributions for each row. The unlabeled final column marks thresholds with *** for p < 0.001, ** for p < 0.01, and * for p < 0.05. Row Highlighting: Light green rows indicate Speedup > 1 and Timing Pval < 0.05; light grey rows indicate NA timing comparisons from a failed fit; light blue rows are estimators with no canonical R implementation at all (only EDI is timed, Canonical Time/Speedup/Timing Pval are NA by design).

EDI faster, significant   NA timing comparison   no canonical R implementation

ClassResponseEDI Time (ms)Canonical PkgCanonical FuncCanonical Time (ms)SpeedupTiming Pval
InferenceAllSimpleMeanDiffPooledVarcontinuous0.12statst.test(pool)0.494.2x1.6e-14***
InferenceContinGLMM (pairs)continuous0.14Noneno canonical R implementationNANANA
InferenceContinLincontinuous0.43statslm.fit(interact)+Wald0.741.73x0.524
InferenceContinOLScontinuous0.02statslm.fit+Wald0.094.01x2.02e-29***
InferenceContinRobustRegrcontinuous0.06MASSrlm+summary1.6927.33x2.29e-28***
InferenceIncidGCompRiskDiffincidence0.10statsglm+gcomp(RD)+Wald3.3533.73x2.35e-22***
InferenceIncidGCompRiskRatioincidence0.09statsglm+gcomp(RR)+Wald2.9431.1x5.47e-22***
InferenceIncidKKCondLogitGLMMOneLikincidence5.37Noneno canonical R implementationNANANA
InferenceIncidLogBinomialincidence0.32statsglm.fit+Wald(log)4.8815.17x2.2e-15***
InferenceIncidLogRegrincidence0.08statsglm.fit+Wald0.9811.84x9.55e-28***
InferenceIncidMiettinenNurminenRiskDiffincidence0.01DescToolsBinomDiffCI(mn)0.8760.27x1.67e-27***
InferenceIncidNewcombeRiskDiffincidence0.11DescToolsBinomDiffCI(score)0.928.63x1.23e-30***
InferenceIncidProbitRegrincidence0.12statsglm.fit(probit)+Wald1.2310.52x9.28e-27***
InferenceIncidRiskDiffincidence0.02statsprop.test0.5823.49x7.63e-25***
InferenceCountGLMM (pairs)count2.34Noneno canonical R implementationNANANA
InferenceCountHurdleNegBincount0.44psclhurdle(nb)+summary18.4642.04x1.65e-21***
InferenceCountHurdlePoissoncount0.95psclhurdle+summary13.8214.58x1.73e-31***
InferenceCountHurdlePoisson (pairs)count0.52Noneno canonical R implementationNANANA
InferenceCountKKCondPoissonOneLikcount0.06Noneno canonical R implementationNANANA
InferenceCountNegBincount0.21MASSglm.nb+summary42.00204.13x5.18e-28***
InferenceCountPoissoncount0.08statsglm.fit+Wald1.2415.88x1.07e-30***
InferenceCountQuasiPoissoncount0.06statsglm.fit+Wald(quasi)1.1118.97x5.18e-30***
InferenceCountRobustPoissoncount0.11sandwichglm+vcovHC4.9146.22x1.55e-30***
InferenceCountZeroInflatedNegBincount0.91psclzeroinfl(nb)+summary85.1793.08x5.2e-25***
InferenceCountZeroInflatedPoissoncount1.77psclzeroinfl+summary38.5021.77x3.44e-33***
InferencePropBetaRegrproportion0.38betaregbetareg+summary21.5656.11x1.38e-22***
InferencePropGCompMeanDiffproportion0.09statsglm(quasi)+gcomp+Wald3.0132.99x7.52e-26***
InferencePropZeroOneInflatedBetaRegrproportion1.14Noneno canonical R implementationNANANA
InferenceSurvivalCoxPHRegrsurvival0.21survivalcoxph.fit(breslow)+Wald0.602.81x1.46e-30***
InferenceSurvivalGehanWilcoxsurvival0.02survivalsurvdiff(rho=1)2.20136.34x2.04e-31***
InferenceSurvivalKKWeibullFrailtyOneLiksurvival1.38Noneno canonical R implementationNANANA
InferenceSurvivalLogRanksurvival0.14survivalsurvdiff5.3839.73x2.54e-26***
InferenceSurvivalStratCoxPHRegrsurvival0.54survivalcoxph.fit(strat)+Wald0.801.48x8.88e-16***
InferenceSurvivalWeibullRegrsurvival0.09survivalsurvreg+summary4.0346.66x9.22e-26***
InferenceOrdinalAdjCatLogitRegrordinal0.18VGAMvglm+summary18.38101.95x7.6e-33***
InferenceOrdinalCLMM (pairs)ordinal9.08Noneno canonical R implementationNANANA
InferenceOrdinalContRatioRegrordinal0.09VGAMvglm+summary18.46213.08x2.86e-30***
InferenceOrdinalGCompMeanDiffordinal0.40ordinalclm+gcomp+Wald8.4621x1.57e-31***
InferenceOrdinalGLMM (pairs)ordinal13.26Noneno canonical R implementationNANANA
InferenceOrdinalJonckheereTerpstraTestordinal0.09clinfunjonckheere1.5517.57x1.61e-32***
InferenceOrdinalPropOddsRegrordinal0.21ordinalclm+summary7.0533.35x1.54e-32***
InferenceOrdinalRiditordinal0.06statsmean(ridit)0.417.52x1.59e-30***
InferenceOrdinalStereotypeLogitRegrordinal0.33Noneno canonical R implementationNANANA

Garbage Collection and Cache Management

To ensure that the benchmark results are highly precise, reproducible, and represent the actual computation speed of the numerical solvers, the benchmarking harness uses the following garbage collection and cache management strategies:

1. Garbage Collection (GC) Filtering

Garbage collection cycles run automatically by the R interpreter and can introduce significant, arbitrary pauses that skew timing measurements. To isolate the execution time of the code from R's GC overhead:

2. Cold-Start Guarantee for EDI and Symmetric Warm-Up for Both Sides

Both EDI and canonical timing expressions receive a single validation/warm-up call executed once before the calibration loop begins. This puts the machine code and working data into the instruction and data caches in the same warmed state for both sides, so the official timed replicates start on equal footing.

EDI timings call exported C++ functions directly — no R6 objects are instantiated during benchmarking. As a result, no R6 result caches exist to manage. Each call to the C++ solver (e.g. fast_logistic_regression_cpp, fast_ordinal_regression_cpp) starts from a freshly zero-initialized parameter vector (or a model-specific data-driven initialization when smart_cold_start = TRUE). No prior-fit results are carried across timing repetitions, so every replication is a genuine cold start for the numerical optimizer.

Utility / Math Kernel Performance

This table is an exhaustive inventory of every fast_* scalar math kernel in EDI/src — used inside the NegBin/Beta/ZINB/Hurdle likelihoods, KK21 negative-binomial fitting, probit cold-start heuristics, ordinal cloglog/cauchit link derivatives, and LRT/score-test p-values — against base R's vectorized equivalents. Unlike the model-fit tables above, these are not full estimators: each row is a single vectorized special-function evaluation (digamma, trigamma, log-gamma, log-beta, error function, normal CDF/PDF/quantile (both closed-form and log-scale), chi-squared upper-tail p-value, arctangent, softplus, and the mu-parameterized negative-binomial density) over a fixed-length vector, with no design matrix, optimizer, or R6 object involved on either side. Vector length: All rows evaluate the function over a vector of length $N=5000$; inputs are drawn from a domain realistic for each kernel's actual call sites in EDI (e.g. shape/rate-like values in $(0.5, 50)$ for digamma/trigamma/lgamma, probabilities in $(0, 1)$ for qnorm). Bare Metal EDI Timing: Eight kernels (digamma, trigamma, lgamma, lbeta, dnbinom_mu, qnorm, log_pnorm, log_dnorm) call the package's own exported fast_*_vec_cpp wrapper (EDI/src/fast_math_utils.cpp, documented and exported via roxygen/NAMESPACE like any other EDI function) directly from the installed package — no separate compile step. The remaining six (pchisq_upper, erfc, pnorm_fast, dnorm_fast, atan, log1pexp) aren't promoted to package exports yet, so those rows call a thin bare-metal wrapper compiled standalone via Rcpp::sourceCpp() against the same EDI/src leaf headers (benchmark/fast_math_utils_bench.cpp) — same convention as benchmark/fast_trigamma_speed_compare.cpp. Either way, the wrapper just loops the internal fast_* scalar kernel over the input vector — no R6, no caching, no warm starts. Canonical R Timing: The base R column calls the corresponding vectorized base/stats function directly on the same input vector (e.g. digamma(), lgamma(), stats::qnorm(), stats::dnbinom(..., mu = , log = TRUE)). Timing Note: All timings use the same adaptive batched system.time/microbenchmark harness as the tables above (medians over 30 cold samples; paths below 0.01 ms fall back to microbenchmark(times = 5000)). Timing P-Value: Timing Pval reports a Welch two-sample t-test comparing the EDI and base R timing replicate distributions for each row. The unlabeled final column marks thresholds with *** for p < 0.001, ** for p < 0.01, and * for p < 0.05. Row Highlighting: Light green rows indicate Speedup > 1 and Timing Pval < 0.05; light grey rows indicate NA timing comparisons from a failed evaluation.

EDI faster, significant   NA timing comparison   no canonical R implementation

FunctionResponseEDI Time (ms)Canonical PkgCanonical FuncCanonical Time (ms)SpeedupTiming Pval
dnorm_fastutility0.05base/statsdnorm0.132.62x3.77e-24***
fast_atanutility0.06base/statsatan0.122.12x3.25e-21***
fast_digammautility0.08base/statsdigamma0.556.78x3.44e-39***
fast_dnbinom_muutility0.47base/statsdnbinom(mu=, log=TRUE)0.641.35x4.61e-14***
fast_erfcutility0.11base/stats2*pnorm(-x*sqrt(2))0.353.17x1.37e-30***
fast_lbetautility0.28base/statslbeta0.692.43x8.8e-44***
fast_lgammautility0.09base/statslgamma0.202.18x6.35e-19***
fast_log1pexputility0.11base/statslog1p(exp(x))0.151.43x3.31e-13***
fast_log_dnormutility0.02base/statsdnorm(log=TRUE)0.095x3.14e-29***
fast_log_pnormutility0.14base/statspnorm(log.p=TRUE)0.352.49x1.52e-28***
fast_pchisq_upperutility0.65base/statspchisq(lower.tail=FALSE)0.981.51x1.77e-28***
fast_qnormutility0.05base/statsqnorm0.112.33x1.03e-12***
fast_trigammautility0.04base/statstrigamma0.7119.3x5.84e-33***
pnorm_fastutility0.09base/statspnorm0.323.37x8.02e-31***