Multithreaded AES-XTS decryption (OpenMP) #4

Merged
zecoxao merged 1 commit from multithreaded-openmp into master 2026-09-15 16:24:17 -05:00
zecoxao commented 2026-09-15 16:23:40 -05:00 (Migrated from github.com)

Summary

Makes ps4encdec multithreaded. The per-1 MiB work blocks are independent — each block's AES-XTS tweak depends only on the absolute sector index — so the main loop is embarrassingly parallel.

  • Distribute blocks across threads with an OpenMP parallel for. The single aes_xts context is initialized once and only read while crypting, so it is shared read-only across threads (the AES tables are static const).
  • Each thread owns its input handle and buffer: it seeks to its block offset, reads, decrypts, and writes. Writes go through one shared output handle under a critical section with an explicit seek, so the result is correct regardless of completion order (crypting, the hot path, stays outside the lock).
  • Thread count honors OMP_NUM_THREADS or an optional [threads] argument:
    ps4encdec <keys> <input> <output> <ivoffset> [threads]
  • Makefile gains -fopenmp.

Latent bugs fixed along the way

  • >4 GiB overflow: offsets/size now use 64-bit _ftelli64/_fseeki64 instead of 32-bit unsigned long (matters for HDD-partition-sized inputs).
  • Garbage tail: a partial final block used to write a full buffer of stale data; it now reads/decrypts/writes the exact remaining bytes, so output size equals input size.
  • Keys and file size are read once instead of on every iteration.

Verification

  • Bit-identical to the previous single-threaded output on aligned input (md5 match).
  • Deterministic across 1/2/4/8/12/16 threads.
  • Correct partial-tail handling (output size = input size).
  • ~4.5× speedup on 256 MiB (820 ms → 180 ms, 12 cores; MinGW gcc 13, -O2 -fopenmp). Past ~8 threads it becomes I/O/write-lock bound, as expected for a decrypt tool.

🤖 Generated with Claude Code

## Summary Makes `ps4encdec` multithreaded. The per-1 MiB work blocks are independent — each block's AES-XTS tweak depends only on the absolute sector index — so the main loop is embarrassingly parallel. - Distribute blocks across threads with an **OpenMP** parallel for. The single `aes_xts` context is initialized once and only read while crypting, so it is shared read-only across threads (the AES tables are `static const`). - Each thread owns its input handle and buffer: it seeks to its block offset, reads, decrypts, and writes. Writes go through one shared output handle under a `critical` section with an explicit seek, so the result is correct regardless of completion order (crypting, the hot path, stays outside the lock). - Thread count honors `OMP_NUM_THREADS` or an optional `[threads]` argument: `ps4encdec <keys> <input> <output> <ivoffset> [threads]` - `Makefile` gains `-fopenmp`. ## Latent bugs fixed along the way - **>4 GiB overflow:** offsets/size now use 64-bit `_ftelli64`/`_fseeki64` instead of 32-bit `unsigned long` (matters for HDD-partition-sized inputs). - **Garbage tail:** a partial final block used to write a full buffer of stale data; it now reads/decrypts/writes the exact remaining bytes, so output size equals input size. - Keys and file size are read once instead of on every iteration. ## Verification - **Bit-identical** to the previous single-threaded output on aligned input (md5 match). - **Deterministic** across 1/2/4/8/12/16 threads. - Correct partial-tail handling (output size = input size). - ~**4.5× speedup** on 256 MiB (820 ms → 180 ms, 12 cores; MinGW gcc 13, `-O2 -fopenmp`). Past ~8 threads it becomes I/O/write-lock bound, as expected for a decrypt tool. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No description provided.