# 6. Why this module's work is CPU-bound, not I/O-bound [← Previous](05-the-diagnostic-endpoint.md) | [README](../README.md) | [Next: The RejectedExecutionException →](07-the-rejectedexecutionexception.md) ## The design choice [`RiskScoringProcessor`](../src/main/java/com/ankurm/batchpartition/processing/RiskScoringProcessor.java) does `risk.iterations` (150 by default) real 64-bit XOR/multiply operations per order, then flags the order high-risk if the resulting score exceeds a threshold or the amount exceeds `risk.high-risk-amount-cents`. No network call, no sleep, no artificial delay — every millisecond it spends is spent computing, on whichever core its thread is scheduled on. That is deliberate. A partitioning demo built around an I/O-bound step (a network call per item, a slow downstream service) shows a speedup even on a single-core machine, because the threads spend almost all their time blocked, not competing for CPU — the "parallelism" it demonstrates is really just concurrency hiding latency, which is a real and useful thing but a different claim than "this uses more of the machine's compute." A CPU-bound processor makes the speedup measured in [`docs/output/09-full-scale-throughput.txt`](output/09-full-scale-throughput.txt) mean what it looks like it means: more cores actually doing more arithmetic per second, capped by how many cores physically exist. ## Determinism, and what it buys [`RiskScoringProcessorTest`](../src/test/java/com/ankurm/batchpartition/processing/RiskScoringProcessorTest.java) pins that the same `Order` always produces the same score: ```console order: Order[orderId=42, customerId=777, amountCents=1234567, region=NORTH] first.process() -> riskScore=61 highRisk=false second.process() -> riskScore=61 highRisk=false ``` Full transcript: [`docs/output/01-processor-determinism.txt`](output/01-processor-determinism.txt). Determinism matters for a partitioned job specifically because of [chapter 8](08-restart-reruns-only-the-failed-partition.md): a partition that fails and re-runs must produce the same output the second time, or a restart silently changes results depending on which attempt happened to write. `MERGE ... KEY(order_id)` in the writer ([chapter 4](04-the-writer-and-the-beanmapped-trap.md)) handles the "don't duplicate the row" half of that; a deterministic processor handles the "don't change the row's *content* between attempts" half. ## What the CPU cost does and does not explain about the scaling numbers Raising `risk.iterations` to 5000 (thirty-three times the default) at a fixed 300,000-row data volume moved the grid-size-2 speedup from 1.10x to 1.26x — see [`docs/output/09-full-scale-throughput.txt`](output/09-full-scale-throughput.txt) for both numbers side by side. That is evidence that at least part of the shortfall from a clean 2x speedup on 2 cores is time spent somewhere that does *not* scale with thread count — the shared H2 writer is the leading candidate, discussed further in [chapter 10](10-scaling-sensitivity-to-data-size.md) — but it is not proof by itself; this module did not isolate the writer completely (by, say, writing to per-partition tables and comparing) to rule out GC pressure or thread scheduling overhead as contributing causes too. Read the 1.10x-to-1.26x shift as "consistent with the write-contention hypothesis," not as a closed case. ## Going deeper - Amdahl's law, for the general shape of "some fraction of the work cannot be parallelized": [Wikipedia — Amdahl's law](https://en.wikipedia.org/wiki/Amdahl%27s_law) (`rel="nofollow"`). [Next: The RejectedExecutionException →](07-the-rejectedexecutionexception.md)