Skip to content

Repository files navigation

Deléglise–Rivat $O(X^{2/3})$ Mertens Engine

A self-contained, header-only C++20 implementation of the Deléglise–Rivat combinatorial method with inclusion–exclusion reductions and micro-architectural optimizations for evaluating the Mertens function: $$ M(X) = \sum_{n=1}^X \mu(n) $$

Based on and extending the mathematical framework described in Greg Hurst's preprint ("Practical Computations of the Mertens Function: $M(10^{24})$ and $M(10^{25})$", arXiv:2607.07566).


Key Features & Optimizations

  1. Inclusion–Exclusion Reductions:

    • Outer Parity Split & Mod-6 Wheel Reduction: Evaluates $S(y, u) - S(y/2, u) - S(y/3, u) + S(y/6, u)$ across square-free $k$ coprime to 6, cutting outer terms by $>69%$.
    • $S_1$ Parity Cancellation: Cancels even quotient lookups across surviving odd/even intervals (stride-2 queries).
    • $S_2$ Piecewise Reduction: Partitions $j \le A, (j, 6)=1$ into 8 exact branchless arithmetic intervals.
  2. Compressed Hierarchical Mertens Table (CHMT) Algorithm (Unified Engine for all $X > 10^5$):

    • Seamless Multi-Tier Scaling: Dynamically sizes u_dense = std::min(u_total, 600M). For $X \le 10^{14}$ ($u \le 600\text{M}$), it operates as a 100% dense table with 0 compressed chunks (zero overhead). For massive $X \ge 10^{15}$, it seamlessly engages Tier 2 (2-bit bit-plane chunks), scaling $u$ up to 8 Billion entries in ~3.1 GB RAM.
    • 13.3× Outer Term Reduction: Slashes the outer summation workload for $10^{16}$ from 16.7 million terms down to 1.25 million terms ($N = X / u$).
    • Constant-Time Hardware Popcount Reconstruction: Evaluates $M(q)$ in single-digit CPU clock cycles via 4 branchless 64-bit hardware popcount instructions (__builtin_popcountll(nz & ~sg) and __builtin_popcountll(nz & sg)), requiring zero memory decompression.
    • Branchless 8-Element Bit-Plane Packing: Exploits $\mu(8k+4)=\mu(8k+8)=0$ and $\mu(8k+2)=-\mu_{\text{odd}}[2k+1]$ to pack 8 numbers per iteration directly from odd sieve bytes, achieving a 13× speedup in table compression.
    • Compile-Time Reciprocal Prime Sieving: Replaces runtime 8-cycle UDIV instructions for small primes ($p \in {3, 5, \dots, 71}$) with 1-cycle UMULH compile-time fixed-point reciprocals (sieve_prime_const<P>).
    • Two-Tier $S_1$ Execution Engine: Resolves initial large quotients via compressed popcount reconstruction and the remaining 99.9% of quotients ($q \le u_{\text{dense}}$) through an 8-way unrolled direct read from M_dense.
  3. Micro-Architectural & SIMD Acceleration:

    • Zero-Allocation Compact Sieve (FastSieve): Generates $M(n)$ prefix sums directly from odd-sieved segments via sequential 64-bit quad writes (int16_t quads), capping the $\mu$ table allocation to $&lt;40\text{ MB}$ ($A_{\max}$) instead of $u$ (600 MB) and saving $&gt;1.2\text{ GB}$ of DRAM traffic.
    • 4-Way Pipelined NEON $S_2$ Runner: Unrolled 4-way (8 coprime-to-6 values per iteration), maintaining $j$ strictly in SIMD registers with vector step increments (vaddq_f64), pipelining 4 vdivq_f64 instructions, and using vcvtq_s64_f64 hardware conversions.
    • Pure SIMD $S_1$ and Even-$n$ Correction: Eliminates division overhead from software prefetching and evaluates $S_1$ arithmetic with SIMD vector additions and hardware integer truncation.
    • Unified Dynamic Parallel Dispatch: Merges all 4 OpenMP ranges into a single unified term vector with dynamic chunk scheduling (schedule(dynamic, 8)), eliminating barrier syncs and fixing thread starvation on small $k$.
    • Flat 16-Bit Cache (int16_t): Uses single-cycle ldrh lookups without multi-level cache table indirections.
    • Dynamic Hardware-Aware $c_x(X)$ Tuning: Automatically balances $S_1$ memory traffic and $S_2$ streaming throughput ($c_x = 0.95$ for $10^{15..16}$, $0.95$ for $10^{11..14}$, $0.70$ for $X \le 10^{10}$).

Build & Test

Prerequisites

  • Clang / GCC supporting C++20
  • OpenMP (libomp via Homebrew on macOS: brew install libomp)

Compilation

make

Running Unit Tests

make test
# Or:
./test_dr

Running Benchmarks

# Evaluate a single value:
./bench_dr 1000000000000000 8

# Benchmark sweep from 10^1 to 10^15:
./bench_dr --benchmark 15 8

# Comparative benchmark vs O(X^(3/4)) Dirichlet DP Engine:
./bench_dr --compare 14 8

CUDA GPU Acceleration (NVIDIA T4 16GB / sm_75)

The engine provides CUDA backend support with GPU-accelerated $S_1$ and $S_2$ evaluations:

  • __ldg Read-Only Caching: Directly streams lookups of $M(n)$ from VRAM (~900MB).
  • __constant__ Memory LUT Evaluators: Fast broadcast caching for $S_2$ piecewise summands with zero register pressure.
  • Branchless Piecewise $S_2$ Modulo-6: Evaluates arithmetic ranges without branch divergence.
  • Warp-Level Parallel Reduction: Uses __shfl_down_sync and shared memory tree reductions for high-occupancy 64-bit atomic accumulation.

CUDA Compilation

# Build CUDA binaries targeting NVIDIA T4 (sm_75):
make cuda

# Or specify a custom architecture (e.g. sm_80 for A100, sm_89 for RTX 4090, sm_90 for H100):
make cuda CUDA_ARCH=sm_80

Running CUDA Tests & Benchmarks

# Run CUDA unit test suite:
make test-cuda

# Evaluate a single value on GPU with detailed timing breakdown:
./bench_cuda 1000000000000000

# Run CUDA benchmark sweep:
./bench_cuda --benchmark 16

# Compare CPU vs CUDA GPU performance:
./bench_cuda --compare 16

Benchmark Results

CPU Performance & Comparison vs. Greg Hurst (arXiv:2607.07566)

Measured 2026-09-20 on Apple M1 Mac mini (8T, 16GB) with the current engine (scalar double-division quotient loops by default with opt-in NEON via MERTENS_USE_NEON_DIV, small-prime {3,5,7,11} sieve stencil, tunable OpenMP chunk MERTENS_DYN_CHUNK=64). MERTENS_FX/MERTENS_CX env overrides select the sieve balance fx and split cx; defaults are fx=0.32 (cap 1.5G) and cx=1.35 ($\ge 10^{15}$) / 1.15 ($\ge 10^{13}$) / 0.95 ($\ge 10^{11}$) / 0.70 (below). Best 2026-09-20 tuning on this box for $10^{15}$ is MERTENS_FX=0.2 MERTENS_CX=1.15. Expect $\pm 10$–$15%$ run-to-run variance (frequency ramp / heat soak on Apple Silicon); figures below are single-sweep runs with ./bench_dr --benchmark 15 8.

Target $X$ Exact $M(X)$ Baseline (Apple M1, 8T) Current Engine, default config (Apple M1, 8T) Tuned FX=0.2 CX=1.15 (Apple M1, 8T) Greg Hurst Preprint (Reported) Speedup (best M1 vs Baseline)
$10^8$ $1,928$ $0.0021$ s $0.0004$ s $0.0007$ s — $3.0\times$
$10^9$ $-222$ $0.0062$ s $0.0008$ s $0.0011$ s — $5.6\times$
$10^{10}$ $-33,722$ $0.0151$ s $0.0040$ s $0.0044$ s — $3.4\times$
$10^{11}$ $-87,856$ $0.0548$ s $0.0125$ s $0.0111$ s — $4.9\times$
$10^{12}$ $62,366$ $0.1030$ s $0.0487$ s $0.0572$ s $\sim 0.03$ s (M2 Max) $1.8\times$
$10^{13}$ $599,582$ $0.4500$ s $0.2212$ s $0.2526$ s $0.12$ s (M2 Max) $1.8\times$
$10^{14}$ $-875,575$ $2.3370$ s $1.1768$ s $1.2620$ s $0.51$ s (M2 Max) $1.9\times$
$10^{15}$ $-3,216,373$ $14.8000$ s $7.7940$ s $7.0379$ s (best observed $5.88$ s cold) $2.10$ s (M2 Max) $2.1\times$
$10^{16}$ $-3,195,437$ $69.6000$ s $50.4758$ s — (not re-run tuned) $2.39$ s (M3 Ultra) $1.4\times$

Small inputs (same 2026-09-20 sweep, default vs tuned — tuning is a no-op below $u = X$, shown for completeness):

Target $X$ Exact $M(X)$ Default config Tuned FX=0.2 CX=1.15
$10^1$ $-1$ $0.0006$ s $0.0006$ s
$10^2$ $1$ $&lt;0.0001$ s $&lt;0.0001$ s
$10^3$ $2$ $&lt;0.0001$ s $&lt;0.0001$ s
$10^4$ $-23$ $&lt;0.0001$ s $&lt;0.0001$ s
$10^5$ $-48$ $0.0003$ s $0.0003$ s
$10^6$ $212$ $0.0025$ s $0.0039$ s
$10^7$ $1,037$ $0.0156$ s $0.0174$ s

NVIDIA Tesla T4 (16GB VRAM, Turing sm_75 - CUDA Benchmark Sweep)

Retained from the earlier T4 run — not re-measured here (no CUDA-capable device on the Apple M1 host used above).

Target $X$ Exact $M(X)$ Sieve Time (s) H2D Copy (s) GPU Kernel (s) Total Time (s)
$10^8$ $1,928$ $0.0016$ $0.0002$ $0.00607$ $0.1778$
$10^9$ $-222$ $0.0081$ $0.0004$ $0.00538$ $0.0141$
$10^{10}$ $-33,722$ $0.0306$ $0.0011$ $0.01121$ $0.0435$
$10^{11}$ $-87,856$ $0.1409$ $0.0046$ $0.02856$ $0.1756$
$10^{12}$ $62,366$ $0.6833$ $0.0215$ $0.11964$ $0.8267$
$10^{13}$ $599,582$ $4.1615$ $0.1126$ $0.28687$ $4.5677$
$10^{14}$ $-875,575$ $8.9891$ $0.2537$ $0.73207$ $9.9804$
$10^{15}$ $-3,216,373$ $9.8777$ $0.2634$ $4.08156$ $14.2351$
$10^{16}$ $-3,195,437$ $9.2805$ $0.2868$ $35.62498$ $45.2675$
$10^{17}$ $-21,830,254$ $10.0107$ $0.4033$ $362.09725$ $373.3884$

About

A self-contained implementation of the Deléglise–Rivat combinatorial method with inclusion–exclusion reductions for evaluating the Mertens function

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages