How it works
Quicksort variant using block partitioning to reduce branch mispredictions.
Implementation
function blockQuickSort(arr, stats) { const n = arr.length; if (n < 2) return; const BLOCK_SIZE = 16; const INSERTION_THRESHOLD = 24; const metrics = stats.educational.metrics; let finalized = 0; const recordMetric = (name, delta = 1) => { metrics[name] = (metrics[name] || 0) + delta; }; metrics.blockSize = BLOCK_SIZE; metrics.insertionThreshold = INSERTION_THRESHOLD; function swap(i, j) { if (i === j) return; swap(i, j); const t = arr[i]; arr[i] = arr[j]; arr[j] = t; recordMetric('pairSwaps'); } function compareIndices(i, j) { compare(i, j); if (arr[i] < arr[j]) return -1; if (arr[i] > arr[j]) return 1; return 0; } function compareAndSwap(i, j) { if (compareIndices(i, j) > 0) swap(i, j); } function markIndex(index) { markSorted(index); finalized++; if (finalized > n) finalized = n; checkpoint(finalized, n); } function markRange(lo, hi) { for (let i = lo; i <= hi; i++) markSorted(i); finalized += hi - lo + 1; if (finalized > n) finalized = n; checkpoint(finalized, n); recordMetric('finalizedRanges'); } function insertionSort(lo, hi) { recordMetric('insertionSortCalls'); for (let i = lo + 1; i <= hi; i++) { const value = arr[i]; let j = i; while (j > lo) { compare(j - 1, i); if (arr[j - 1] <= value) break; arr[j] = arr[j - 1]; recordMetric('writeOperations'); write(j, arr[j]); j--; } arr[j] = value; recordMetric('writeOperations'); write(j, value); } markRange(lo, hi); } function choosePivot(lo, hi) { const mid = lo + (hi - lo >> 1); recordMetric('pivotSelections'); compareAndSwap(lo, mid); compareAndSwap(mid, hi); compareAndSwap(lo, mid); swap(mid, hi); return hi; } function partition(lo, hi) { recordMetric('partitionCalls'); const pivotIndex = choosePivot(lo, hi); const pivot = arr[pivotIndex]; let left = lo; let right = hi - 1; const leftOffsets = new Array(BLOCK_SIZE); const rightOffsets = new Array(BLOCK_SIZE); let leftCount = 0; let rightCount = 0; while (right - left + 1 > BLOCK_SIZE * 2) { if (leftCount === 0) { recordMetric('leftBlockScans'); for (let i = 0; i < BLOCK_SIZE; i++) { const idx = left + i; compare(idx, hi); if (arr[idx] >= pivot) { leftOffsets[leftCount] = i; leftCount++; } } } if (rightCount === 0) { recordMetric('rightBlockScans'); for (let i = 0; i < BLOCK_SIZE; i++) { const idx = right - i; compare(idx, hi); if (arr[idx] < pivot) { rightOffsets[rightCount] = i; rightCount++; } } } const pairCount = leftCount < rightCount ? leftCount : rightCount; for (let i = 0; i < pairCount; i++) { swap(left + leftOffsets[i], right - rightOffsets[i]); } if (pairCount > 0) recordMetric('blockSwapBatches'); if (leftCount === pairCount) { left += BLOCK_SIZE; leftCount = 0; } else { leftCount -= pairCount; for (let i = 0; i < leftCount; i++) leftOffsets[i] = leftOffsets[i + pairCount]; } if (rightCount === pairCount) { right -= BLOCK_SIZE; rightCount = 0; } else { rightCount -= pairCount; for (let i = 0; i < rightCount; i++) rightOffsets[i] = rightOffsets[i + pairCount]; } } while (left <= right) { while (left <= right) { compare(left, hi); if (arr[left] >= pivot) break; left++; } while (left <= right) { compare(right, hi); if (arr[right] < pivot) break; right--; } if (left < right) { swap(left, right); left++; right--; } } swap(left, hi); return left; } const stack = [[0, n - 1]]; while (stack.length) { const range = stack.pop(); let lo = range[0]; let hi = range[1]; while (lo < hi) { if (hi - lo + 1 <= INSERTION_THRESHOLD) { insertionSort(lo, hi); break; } const pivotIndex = partition(lo, hi); markIndex(pivotIndex); const leftSize = pivotIndex - lo; const rightSize = hi - pivotIndex; if (leftSize < rightSize) { if (pivotIndex + 1 < hi) stack.push([pivotIndex + 1, hi]); hi = pivotIndex - 1; } else { if (lo < pivotIndex - 1) stack.push([lo, pivotIndex - 1]); lo = pivotIndex + 1; } } if (lo === hi) markIndex(lo); } checkpoint(n, n); }
def blockQuickSort(arr, stats): def recordMetric(name, ): metrics[name] = ((metrics[name] or 0) + delta) def swap(i, j): if (i == j): return swap(i, j) t = arr[i] arr[i] = arr[j] arr[j] = t recordMetric("pairSwaps") def compareIndices(i, j): compare(i, j) if (arr[i] < arr[j]): return -1 if (arr[i] > arr[j]): return 1 return 0 def compareAndSwap(i, j): if (compareIndices(i, j) > 0): swap(i, j) def markIndex(index): nonlocal finalized markSorted(index) finalized += 1 if (finalized > n): finalized = n checkpoint(finalized, n) def markRange(lo, hi): nonlocal finalized for i in range(lo, (hi + 1)): markSorted(i) finalized += ((hi - lo) + 1) if (finalized > n): finalized = n checkpoint(finalized, n) recordMetric("finalizedRanges") def insertionSort(lo, hi): recordMetric("insertionSortCalls") for i in range((lo + 1), (hi + 1)): value = arr[i] j = i while (j > lo): compare((j - 1), i) if (arr[(j - 1)] <= value): break arr[j] = arr[(j - 1)] recordMetric("writeOperations") write(j, arr[j]) j -= 1 arr[j] = value recordMetric("writeOperations") write(j, value) markRange(lo, hi) def choosePivot(lo, hi): mid = (lo + ((hi - lo) >> 1)) recordMetric("pivotSelections") compareAndSwap(lo, mid) compareAndSwap(mid, hi) compareAndSwap(lo, mid) swap(mid, hi) return hi def partition(lo, hi): recordMetric("partitionCalls") pivotIndex = choosePivot(lo, hi) pivot = arr[pivotIndex] left = lo right = (hi - 1) leftOffsets = [0] * BLOCK_SIZE rightOffsets = [0] * BLOCK_SIZE leftCount = 0 rightCount = 0 while (((right - left) + 1) > (BLOCK_SIZE * 2)): if (leftCount == 0): recordMetric("leftBlockScans") for i in range(BLOCK_SIZE): idx = (left + i) compare(idx, hi) if (arr[idx] >= pivot): leftOffsets[leftCount] = i leftCount += 1 if (rightCount == 0): recordMetric("rightBlockScans") for i in range(BLOCK_SIZE): idx = (right - i) compare(idx, hi) if (arr[idx] < pivot): rightOffsets[rightCount] = i rightCount += 1 pairCount = (leftCount if (leftCount < rightCount) else rightCount) for i in range(pairCount): swap((left + leftOffsets[i]), (right - rightOffsets[i])) if (pairCount > 0): recordMetric("blockSwapBatches") if (leftCount == pairCount): left += BLOCK_SIZE leftCount = 0 else: leftCount -= pairCount for i in range(leftCount): leftOffsets[i] = leftOffsets[(i + pairCount)] if (rightCount == pairCount): right -= BLOCK_SIZE rightCount = 0 else: rightCount -= pairCount for i in range(rightCount): rightOffsets[i] = rightOffsets[(i + pairCount)] while (left <= right): while (left <= right): compare(left, hi) if (arr[left] >= pivot): break left += 1 while (left <= right): compare(right, hi) if (arr[right] < pivot): break right -= 1 if (left < right): swap(left, right) left += 1 right -= 1 swap(left, hi) return left if (n < 2): return BLOCK_SIZE = 16 INSERTION_THRESHOLD = 24 finalized = 0 metrics.blockSize = BLOCK_SIZE metrics.insertionThreshold = INSERTION_THRESHOLD stack = [[0, (n - 1)]] while len(stack): range = stack.pop() lo = range[0] hi = range[1] while (lo < hi): if (((hi - lo) + 1) <= INSERTION_THRESHOLD): insertionSort(lo, hi) break pivotIndex = partition(lo, hi) markIndex(pivotIndex) leftSize = (pivotIndex - lo) rightSize = (hi - pivotIndex) if (leftSize < rightSize): if ((pivotIndex + 1) < hi): stack.append([(pivotIndex + 1), hi]) hi = (pivotIndex - 1) else: if (lo < (pivotIndex - 1)): stack.append([lo, (pivotIndex - 1)]) lo = (pivotIndex + 1) if (lo == hi): markIndex(lo) checkpoint(n, n)
#include <vector> #include <algorithm> void recordMetric(int name, int ); void swap(int i, int j); int compareIndices(int i, int j); void compareAndSwap(int i, int j); void markIndex(int index); void markRange(int lo, int hi); void insertionSort(int lo, int hi); int choosePivot(int lo, int hi); int partition(int lo, int hi); void recordMetric(int name, int ) { metrics[name] = (((metrics[name]) != 0 ? (metrics[name]) : (0)) + delta); } void swap(int i, int j) { if((i == j)) { return; } swap(i, j); int t = arr[i]; arr[i] = arr[j]; arr[j] = t; recordMetric("pairSwaps"); } int compareIndices(int i, int j) { compare(i, j); if((arr[i] < arr[j])) { return -1; } if((arr[i] > arr[j])) { return 1; } return 0; } void compareAndSwap(int i, int j) { if((compareIndices(i, j) > 0)) { swap(i, j); } } void markIndex(int index) { markSorted(index); finalized++; if((finalized > n)) { finalized = n; } checkpoint(finalized, n); } void markRange(int lo, int hi) { for(int i=lo; i<(hi + 1); i++) { markSorted(i); } finalized += ((hi - lo) + 1); if((finalized > n)) { finalized = n; } checkpoint(finalized, n); recordMetric("finalizedRanges"); } void insertionSort(int lo, int hi) { recordMetric("insertionSortCalls"); for(int i=(lo + 1); i<(hi + 1); i++) { int value = arr[i]; int j = i; while((j > lo)) { compare((j - 1), i); if((arr[(j - 1)] <= value)) { break; } arr[j] = arr[(j - 1)]; recordMetric("writeOperations"); write(j, arr[j]); j--; } arr[j] = value; recordMetric("writeOperations"); write(j, value); } markRange(lo, hi); } int choosePivot(int lo, int hi) { int mid = (lo + ((hi - lo) >> 1)); recordMetric("pivotSelections"); compareAndSwap(lo, mid); compareAndSwap(mid, hi); compareAndSwap(lo, mid); swap(mid, hi); return hi; } int partition(int lo, int hi) { recordMetric("partitionCalls"); int pivotIndex = choosePivot(lo, hi); int pivot = arr[pivotIndex]; int left = lo; int right = (hi - 1); std::vector<int> leftOffsets(BLOCK_SIZE, 0); std::vector<int> rightOffsets(BLOCK_SIZE, 0); int leftCount = 0; int rightCount = 0; while((((right - left) + 1) > (BLOCK_SIZE * 2))) { if((leftCount == 0)) { recordMetric("leftBlockScans"); for(int i=0; i<BLOCK_SIZE; i++) { int idx = (left + i); compare(idx, hi); if((arr[idx] >= pivot)) { leftOffsets[leftCount] = i; leftCount++; } } } if((rightCount == 0)) { recordMetric("rightBlockScans"); for(int i=0; i<BLOCK_SIZE; i++) { int idx = (right - i); compare(idx, hi); if((arr[idx] < pivot)) { rightOffsets[rightCount] = i; rightCount++; } } } int pairCount = (((leftCount < rightCount)) ? (leftCount) : (rightCount)); for(int i=0; i<pairCount; i++) { swap((left + leftOffsets[i]), (right - rightOffsets[i])); } if((pairCount > 0)) { recordMetric("blockSwapBatches"); } if((leftCount == pairCount)) { left += BLOCK_SIZE; leftCount = 0; } else { leftCount -= pairCount; for(int i=0; i<leftCount; i++) { leftOffsets[i] = leftOffsets[(i + pairCount)]; } } if((rightCount == pairCount)) { right -= BLOCK_SIZE; rightCount = 0; } else { rightCount -= pairCount; for(int i=0; i<rightCount; i++) { rightOffsets[i] = rightOffsets[(i + pairCount)]; } } } while((left <= right)) { while((left <= right)) { compare(left, hi); if((arr[left] >= pivot)) { break; } left++; } while((left <= right)) { compare(right, hi); if((arr[right] < pivot)) { break; } right--; } if((left < right)) { swap(left, right); left++; right--; } } swap(left, hi); return left; } void sort(std::vector<int>& arr, int n, int& comparisons, int& swaps) { if((n < 2)) { return; } int BLOCK_SIZE = 16; int INSERTION_THRESHOLD = 24; int metrics = educational.metrics; int finalized = 0; metrics.blockSize = BLOCK_SIZE; metrics.insertionThreshold = INSERTION_THRESHOLD; std::vector<int> stack = {{0, (n - 1)}}; while((int)stack.size()) { int range = stack.back(); int lo = range[0]; int hi = range[1]; while((lo < hi)) { if((((hi - lo) + 1) <= INSERTION_THRESHOLD)) { insertionSort(lo, hi); break; } int pivotIndex = partition(lo, hi); markIndex(pivotIndex); int leftSize = (pivotIndex - lo); int rightSize = (hi - pivotIndex); if((leftSize < rightSize)) { if(((pivotIndex + 1) < hi)) { stack.push_back({(pivotIndex + 1), hi}); } hi = (pivotIndex - 1); } else { if((lo < (pivotIndex - 1))) { stack.push_back({lo, (pivotIndex - 1)}); } lo = (pivotIndex + 1); } } if((lo == hi)) { markIndex(lo); } } checkpoint(n, n); }
int _listPop(List<int> _l) { int _v = _l[_l.Count - 1]; _l.RemoveAt(_l.Count - 1); return _v; } void recordMetric(int name, int ) { metrics[name] = (((metrics[name]) != 0 ? (metrics[name]) : (0)) + delta); } void swap(int i, int j) { if((i == j)) { return; } swap(i, j); int t = arr[i]; arr[i] = arr[j]; arr[j] = t; recordMetric("pairSwaps"); } int compareIndices(int i, int j) { compare(i, j); if((arr[i] < arr[j])) { return -1; } if((arr[i] > arr[j])) { return 1; } return 0; } void compareAndSwap(int i, int j) { if((compareIndices(i, j) > 0)) { swap(i, j); } } void markIndex(int index) { markSorted(index); finalized++; if((finalized > n)) { finalized = n; } checkpoint(finalized, n); } void markRange(int lo, int hi) { for(int i=lo; i<(hi + 1); i++) { markSorted(i); } finalized += ((hi - lo) + 1); if((finalized > n)) { finalized = n; } checkpoint(finalized, n); recordMetric("finalizedRanges"); } void insertionSort(int lo, int hi) { recordMetric("insertionSortCalls"); for(int i=(lo + 1); i<(hi + 1); i++) { int value = arr[i]; int j = i; while((j > lo)) { compare((j - 1), i); if((arr[(j - 1)] <= value)) { break; } arr[j] = arr[(j - 1)]; recordMetric("writeOperations"); write(j, arr[j]); j--; } arr[j] = value; recordMetric("writeOperations"); write(j, value); } markRange(lo, hi); } int choosePivot(int lo, int hi) { int mid = (lo + ((hi - lo) >> 1)); recordMetric("pivotSelections"); compareAndSwap(lo, mid); compareAndSwap(mid, hi); compareAndSwap(lo, mid); swap(mid, hi); return hi; } int partition(int lo, int hi) { recordMetric("partitionCalls"); int pivotIndex = choosePivot(lo, hi); int pivot = arr[pivotIndex]; int left = lo; int right = (hi - 1); int[] leftOffsets = new int[BLOCK_SIZE]; int[] rightOffsets = new int[BLOCK_SIZE]; int leftCount = 0; int rightCount = 0; while((((right - left) + 1) > (BLOCK_SIZE * 2))) { if((leftCount == 0)) { recordMetric("leftBlockScans"); for(int i=0; i<BLOCK_SIZE; i++) { int idx = (left + i); compare(idx, hi); if((arr[idx] >= pivot)) { leftOffsets[leftCount] = i; leftCount++; } } } if((rightCount == 0)) { recordMetric("rightBlockScans"); for(int i=0; i<BLOCK_SIZE; i++) { int idx = (right - i); compare(idx, hi); if((arr[idx] < pivot)) { rightOffsets[rightCount] = i; rightCount++; } } } int pairCount = (((leftCount < rightCount)) ? (leftCount) : (rightCount)); for(int i=0; i<pairCount; i++) { swap((left + leftOffsets[i]), (right - rightOffsets[i])); } if((pairCount > 0)) { recordMetric("blockSwapBatches"); } if((leftCount == pairCount)) { left += BLOCK_SIZE; leftCount = 0; } else { leftCount -= pairCount; for(int i=0; i<leftCount; i++) { leftOffsets[i] = leftOffsets[(i + pairCount)]; } } if((rightCount == pairCount)) { right -= BLOCK_SIZE; rightCount = 0; } else { rightCount -= pairCount; for(int i=0; i<rightCount; i++) { rightOffsets[i] = rightOffsets[(i + pairCount)]; } } } while((left <= right)) { while((left <= right)) { compare(left, hi); if((arr[left] >= pivot)) { break; } left++; } while((left <= right)) { compare(right, hi); if((arr[right] < pivot)) { break; } right--; } if((left < right)) { swap(left, right); left++; right--; } } swap(left, hi); return left; } public void Sort(int[] arr, int n, dynamic stats) { if((n < 2)) { return; } int BLOCK_SIZE = 16; int INSERTION_THRESHOLD = 24; int metrics = stats.educational.metrics; int finalized = 0; metrics.blockSize = BLOCK_SIZE; metrics.insertionThreshold = INSERTION_THRESHOLD; var stack = new List<int> { new int[] {0, (n - 1)} }; while(stack.Count) { int range = _listPop(stack); int lo = range[0]; int hi = range[1]; while((lo < hi)) { if((((hi - lo) + 1) <= INSERTION_THRESHOLD)) { insertionSort(lo, hi); break; } int pivotIndex = partition(lo, hi); markIndex(pivotIndex); int leftSize = (pivotIndex - lo); int rightSize = (hi - pivotIndex); if((leftSize < rightSize)) { if(((pivotIndex + 1) < hi)) { stack.Add(new int[] {(pivotIndex + 1), hi}); } hi = (pivotIndex - 1); } else { if((lo < (pivotIndex - 1))) { stack.Add(new int[] {lo, (pivotIndex - 1)}); } lo = (pivotIndex + 1); } } if((lo == hi)) { markIndex(lo); } } checkpoint(n, n); }
#include <stdio.h> #include <stdlib.h> void recordMetric(int name, int ); void swap(int i, int j); int compareIndices(int i, int j); void compareAndSwap(int i, int j); void markIndex(int index); void markRange(int lo, int hi); void insertionSort(int lo, int hi); int choosePivot(int lo, int hi); int partition(int lo, int hi); void recordMetric(int name, int ) { metrics[name] = (((metrics[name]) != 0 ? (metrics[name]) : (0)) + delta); } void swap(int i, int j) { if((i == j)) { return; } swap(i, j); int t = arr[i]; arr[i] = arr[j]; arr[j] = t; recordMetric("pairSwaps"); } int compareIndices(int i, int j) { compare(i, j); if((arr[i] < arr[j])) { return -1; } if((arr[i] > arr[j])) { return 1; } return 0; } void compareAndSwap(int i, int j) { if((compareIndices(i, j) > 0)) { swap(i, j); } } void markIndex(int index) { markSorted(index); finalized++; if((finalized > n)) { finalized = n; } checkpoint(finalized, n); } void markRange(int lo, int hi) { for(int i=lo; i<(hi + 1); i++) { markSorted(i); } finalized += ((hi - lo) + 1); if((finalized > n)) { finalized = n; } checkpoint(finalized, n); recordMetric("finalizedRanges"); } void insertionSort(int lo, int hi) { recordMetric("insertionSortCalls"); for(int i=(lo + 1); i<(hi + 1); i++) { int value = arr[i]; int j = i; while((j > lo)) { compare((j - 1), i); if((arr[(j - 1)] <= value)) { break; } arr[j] = arr[(j - 1)]; recordMetric("writeOperations"); write(j, arr[j]); j--; } arr[j] = value; recordMetric("writeOperations"); write(j, value); } markRange(lo, hi); } int choosePivot(int lo, int hi) { int mid = (lo + ((hi - lo) >> 1)); recordMetric("pivotSelections"); compareAndSwap(lo, mid); compareAndSwap(mid, hi); compareAndSwap(lo, mid); swap(mid, hi); return hi; } int partition(int lo, int hi) { recordMetric("partitionCalls"); int pivotIndex = choosePivot(lo, hi); int pivot = arr[pivotIndex]; int left = lo; int right = (hi - 1); int* leftOffsets = (int*)malloc((BLOCK_SIZE) * sizeof(int)); int* rightOffsets = (int*)malloc((BLOCK_SIZE) * sizeof(int)); int leftCount = 0; int rightCount = 0; while((((right - left) + 1) > (BLOCK_SIZE * 2))) { if((leftCount == 0)) { recordMetric("leftBlockScans"); for(int i=0; i<BLOCK_SIZE; i++) { int idx = (left + i); compare(idx, hi); if((arr[idx] >= pivot)) { leftOffsets[leftCount] = i; leftCount++; } } } if((rightCount == 0)) { recordMetric("rightBlockScans"); for(int i=0; i<BLOCK_SIZE; i++) { int idx = (right - i); compare(idx, hi); if((arr[idx] < pivot)) { rightOffsets[rightCount] = i; rightCount++; } } } int pairCount = (((leftCount < rightCount)) ? (leftCount) : (rightCount)); for(int i=0; i<pairCount; i++) { swap((left + leftOffsets[i]), (right - rightOffsets[i])); } if((pairCount > 0)) { recordMetric("blockSwapBatches"); } if((leftCount == pairCount)) { left += BLOCK_SIZE; leftCount = 0; } else { leftCount -= pairCount; for(int i=0; i<leftCount; i++) { leftOffsets[i] = leftOffsets[(i + pairCount)]; } } if((rightCount == pairCount)) { right -= BLOCK_SIZE; rightCount = 0; } else { rightCount -= pairCount; for(int i=0; i<rightCount; i++) { rightOffsets[i] = rightOffsets[(i + pairCount)]; } } } while((left <= right)) { while((left <= right)) { compare(left, hi); if((arr[left] >= pivot)) { break; } left++; } while((left <= right)) { compare(right, hi); if((arr[right] < pivot)) { break; } right--; } if((left < right)) { swap(left, right); left++; right--; } } swap(left, hi); return left; } void sort(int arr[], int n, int* comparisons, int* swaps) { if((n < 2)) { return; } int BLOCK_SIZE = 16; int INSERTION_THRESHOLD = 24; int metrics = (*educational).metrics; int finalized = 0; metrics.blockSize = BLOCK_SIZE; metrics.insertionThreshold = INSERTION_THRESHOLD; int* stack = (int*)malloc(n * sizeof(int)); int stack_len = 1; stack[0] = {0, (n - 1)}; while(stack_len) { int range = stack[--stack_len]; int lo = range[0]; int hi = range[1]; while((lo < hi)) { if((((hi - lo) + 1) <= INSERTION_THRESHOLD)) { insertionSort(lo, hi); break; } int pivotIndex = partition(lo, hi); markIndex(pivotIndex); int leftSize = (pivotIndex - lo); int rightSize = (hi - pivotIndex); if((leftSize < rightSize)) { if(((pivotIndex + 1) < hi)) { stack[stack_len++] = {(pivotIndex + 1), hi}; } hi = (pivotIndex - 1); } else { if((lo < (pivotIndex - 1))) { stack[stack_len++] = {lo, (pivotIndex - 1)}; } lo = (pivotIndex + 1); } } if((lo == hi)) { markIndex(lo); } } checkpoint(n, n); }