Parallel Radix Sort: Mechanism, Complexity Analysis & Interactive Visualizer
Parallel Radix Sort
A non-comparison parallel sorting algorithm: counts digit frequencies concurrently across local thread processors, computing prefix sums to place items stably in parallel memory offsets.
How it works
Parallel Radix Sort parallelizes Radix Sort (typically Least Significant Digit - LSD) across multiple threads. Instead of having a single CPU count and write digit buckets, the load is divided into coordinate processors:
- Processor Sub-Partitioning: Divide the array into \(P\) equal blocks (e.g. 2 blocks for 2 simulated processors). Each processor receives a dedicated partition.
- Parallel Local Counting: For each digit place (ones, tens, hundreds...):
- Each processor scans its local block and populates its own **local count array** indicating how many times each digit appears.
- Global Prefix Scan (Reduction): Perform a parallel prefix sum across the count tables of all processors. This prefix sum establishes the exact starting index offset where each processor must write elements for each digit bucket.
- Parallel Memory Writes: Each processor reads its partition, maps each value to its digit, and writes it directly to its computed offset in a temporary output array. The offset is then incremented. Because offsets are disjoint, threads can write concurrently with no race conditions.
- Next Digit Pass: Copy the temporary array back and repeat the cycle for the next digit.
Complexity
Interactive visualization
Pseudocode
procedure parallelRadixSort(A, P): maxVal ← findMax(A) for exp ← 1 to maxVal step exp * radix: # Step 1: Thread-local counts in parallel localCounts ← array of size P × radix filled with 0 parallel for pid ← 0 to P - 1: start, end ← getPartitionBounds(pid, length(A)) localCounts[pid] ← countDigits(A, start, end, exp) # Step 2: Global prefix sum (scan) offsets ← computeGlobalOffsets(localCounts, P) # Step 3: Parallel distribution write temp ← array of size length(A) parallel for pid ← 0 to P - 1: start, end ← getPartitionBounds(pid, length(A)) for i from start to end: digit ← (A[i] / exp) % radix destIdx ← offsets[pid][digit] temp[destIdx] ← A[i] offsets[pid][digit] ← destIdx + 1 copy temp back to A
JavaScript implementation
// Simulating Parallel Radix Sort logic using 2 partition threads function parallelRadixSort(arr) { const n = arr.length; if (n <= 1) return arr; const maxVal = Math.max(...arr); const P = 2; // 2 simulated processors const mid = Math.floor(n / P); for (let exp = 1; Math.floor(maxVal / exp) > 0; exp *= 10) { const count0 = new Array(10).fill(0); const count1 = new Array(10).fill(0); // Processor 0 local count for (let i = 0; i < mid; i++) { const digit = Math.floor(arr[i] / exp) % 10; count0[digit]++; } // Processor 1 local count for (let i = mid; i < n; i++) { const digit = Math.floor(arr[i] / exp) % 10; count1[digit]++; } // Global Prefix Sum computation const pref = new Array(10).fill(0); let cumulative = 0; for (let d = 0; d < 10; d++) { pref[d] = cumulative; cumulative += count0[d] + count1[d]; } // Distribute offsets const offset0 = [...pref]; const offset1 = []; for (let d = 0; d < 10; d++) { offset1.push(pref[d] + count0[d]); } const temp = new Array(n).fill(0); // Processor 0 writes for (let i = 0; i < mid; i++) { const digit = Math.floor(arr[i] / exp) % 10; temp[offset0[digit]++] = arr[i]; } // Processor 1 writes for (let i = mid; i < n; i++) { const digit = Math.floor(arr[i] / exp) % 10; temp[offset1[digit]++] = arr[i]; } for (let i = 0; i < n; i++) arr[i] = temp[i]; } return arr; }
Pros and cons
Pros
- Stunning performance scaling, beating comparison sorts at size scale.
- Highly stable sorting order.
- Highly optimized for GPU/CUDA architectures using warp-level primitives.
Cons
- Requires O(n) auxiliary memory.
- Sensitive to key representation format; not suitable for custom float sorting.
- Sub-counts and prefix sum sync introduces processor overhead on small inputs.
When (not) to use it
- GPU Compute Engines: Broadly used on GPU sorting pipelines (like Thrust in CUDA) where thousands of parallel threads execute prefix scans concurrently.
- Standard CPUs: Avoid on sequential architectures; the extra synchronization steps degrade performance compared to standard Timsort or Quicksort.
Related reading
Sleep Sort: Mechanism, Complexity Analysis & Interactive Visualizer
A unique concurrency-based sorting algorithm: spawns a separate thread for each element, sleeping for a duration proportional to the element value before writing it to the output.
By Md. Harun-Or-Roshid
Cube Sort: Mechanism, Complexity Analysis & Interactive Visualizer
A parallel dimension sorting algorithm: organizes elements in a 3D coordinate cube and sorts vectors along the height, width, and depth axes sequentially to achieve sorted order.
By Md. Harun-Or-Roshid
Patience Sort: Mechanism, Complexity Analysis & Interactive Visualizer
A card-game inspired sorting algorithm: distributes elements onto a series of piles such that each pile's top is \(\ge\) the incoming value, then performs a multi-way merge across pile tops.
By Md. Harun-Or-Roshid
Library Sort: Mechanism, Complexity Analysis & Interactive Visualizer
A gapped variation of Insertion Sort: leaves empty slots (gaps) in the array, allowing elements to be inserted in \(O(1)\) shift time on average by avoiding massive shifts.
By Md. Harun-Or-Roshid