BlockQuicksort

Quicksort variant using block partitioning to reduce branch mispredictions.

Best O(n log n) Avg O(n log n) Worst O(n²) Space O(log n) Stable No In-place Yes Comparison-based

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);
}