HTML Preparation code:
x
 
1
<script>
2
  function swap(ary, a, b) {
3
      var t = ary[a];
4
      ary[a] = ary[b];
5
      ary[b] = t;
6
  }
7
  
8
  // Built-in with comparison function (default sorting is "dictionary-style")
9
  function builtin_sort(ary) {
10
      return ary.sort(function(a, b) {
11
          return a - b;
12
      });
13
  }
14
  
15
  // Bubble Sort
16
  function bubble_sort(ary) {
17
      var a, b;
18
      for (a = 0; a < ary.length; a++) {
19
          for (b = a + 1; b < ary.length; b++) {
20
              if (ary[a] > ary[b]) {
21
                  swap(ary, a, b);
22
              }
23
          }
24
      }
25
  
26
      return ary;
27
  }
28
  //Insertion sort
29
  function insertion_sort(ary) {
30
        for(var i=1,l=ary.length;i<l;i++) {
31
            var value = ary[i];
32
            for(var j=i - 1;j>=0;j--) {
33
                if(ary[j] <= value)
34
                    break;
35
                ary[j+1] = ary[j];
36
            }
37
            ary[j+1] = value;
38
        }
39
        return ary;
40
  }
41
  
42
  // Naive (but understandable) quicksort (memory hog)
43
  function naive_quicksort(ary) {
44
      if (ary.length <= 1) {
45
          return ary;
46
      }
47
  
48
      var less = [],
49
          greater = [],
50
          pivot = ary.pop();
51
  
52
      for (var i = 0; i < ary.length; i++) {
53
          if (ary[i] < pivot) {
54
              less.push(ary[i]);
55
          } else {
56
              greater.push(ary[i]);
57
          }
58
      }
59
  
60
      less = naive_quicksort(less);
61
      greater = naive_quicksort(greater);
62
  
63
      return less.concat(pivot, greater);
64
  }
65
  
66
  // In place quicksort
67
  function inplace_quicksort_partition(ary, start, end, pivotIndex) {
68
      var i = start, j = end;
69
      var pivot = ary[pivotIndex];
70
      
71
      while(true) {
72
          while(ary[i] < pivot) {i++};
73
          j--;
74
          while(pivot < ary[j]) {j--};
75
          if(!(i<j)) {
76
              return i;
77
          }
78
          swap(ary,i,j);
79
          i++;
80
     }
81
  }
82
  
83
  function inplace_quicksort(ary, start, end) {
84
      if (start < end - 1) {
85
          var pivotIndex = Math.round((start + end) / 2);
86
  
87
          pivotIndex = inplace_quicksort_partition(ary, start, end, pivotIndex);
88
  
89
          inplace_quicksort(ary, start, pivotIndex - 1);
90
          inplace_quicksort(ary, pivotIndex + 1, end);
91
      }
92
  
93
      return ary;
94
  }
95
  
96
  // Heap sort
97
  function heapSort(ary) {
98
      heapify(ary);
99
  
100
      for (var end = ary.length - 1; end > 0; end--) {
101
          swap(ary, end, 0);
102
          shiftDown(ary, 0, end - 1);
103
      }
104
  
105
      return ary;
106
  }
107
  
108
  function heapify(ary) {
109
      for (var start = (ary.length >> 1) - 1; start >= 0; start--) {
110
          shiftDown(ary, start, ary.length - 1);
111
      }
112
  }
113
  
114
  function shiftDown(ary, start, end) {
115
      var root = start,
116
          child, s;
117
  
118
      while (root * 2 + 1 <= end) {
119
          child = root * 2 + 1;
120
          s = root;
121
  
122
          if (ary[s] < ary[child]) {
123
              s = child;
124
          }
125
  
126
          if (child + 1 <= end && ary[s] < ary[child + 1]) {
127
              s = child + 1;
128
          }
129
  
130
          if (s !== root) {
131
              swap(ary, root, s);
132
              root = s;
133
          } else {
134
              return;
135
          }
136
      }
137
  }
138
  
139
  // Merge sort
140
  function merge_sort(ary) {
141
      if (ary.length <= 1) {
142
          return ary;
143
      }
144
  
145
      var m = ary.length >> 1;
146
  
147
      var left = ary.slice(0, m),
148
          right = ary.slice(m);
149
  
150
      return merge(merge_sort(left), merge_sort(right));
151
  }
152
  
153
  function merge(left, right) {
154
      var result = [],
155
          li = 0, ri = 0;
156
  
157
      while (li < left.length || ri < right.length) {
158
          if (li < left.length && ri < right.length) {
159
              if (left[li] <= right[ri]) {
160
                  result.push(left[li]);
161
                  li++;
162
              } else {
163
                  result.push(right[ri]);
164
                  ri++;
165
              }
166
          } else if (li < left.length) {
167
              result.push(left[li]);
168
              li++;
169
          } else if (ri < right.length) {
170
              result.push(right[ri]);
171
              ri++;
172
          }
173
      }
174
  
175
      return result;
176
  }
177
  
178
  // Shell sort
179
  function shell_sort(ary) {
180
      var inc = Math.round(ary.length / 2),
181
          i, j, t;
182
  
183
      while (inc > 0) {
184
          for (i = inc; i < ary.length; i++) {
185
              t = ary[i];
186
              j = i;
187
              while (j >= inc && ary[j - inc] > t) {
188
                  ary[j] = ary[j - inc];
189
                  j -= inc;
190
              }
191
              ary[j] = t;
192
          }
193
          inc = Math.round(inc / 2.2);
194
      }
195
  
196
      return ary;
197
  }
198
  //Comb Sort (Basically bubblesort with a small modification, but heaps faster)
199
function comb_sort(ary) {
200
    var gap = ary.length;
201
    while(true) {
202
        gap = newgap(gap);
203
        var swapped = false;
204
        for(var i=0,l=ary.length;i<l;i++) {
205
            var j = i + gap;
206
            if(ary[i] < ary[j]) {
207
                swap(ary,i,j);
208
                swapped = true;
209
            }
210
        }
211
        if(gap == 1 && !swapped)
212
             break;
213
    }
214
    return ary;   
215
}
216
function newgap(gap) {
217
    gap = ((gap * 10) / 13) | 0;
218
    if(gap == 9 || gap == 10)
219
        gap = 11;
220
    if(gap < 1)
221
         gap = 1;
222
    return gap;
223
}
224
//faster quicksort using a stack to eliminate recursion, sorting inplace to reduce memory usage, and using insertion sort for small partition sizes
225
function fast_quicksort(ary) {
226
    var stack = [];
227
    var entry = [0,ary.length,2 * Math.floor(Math.log(ary.length)/Math.log(2))];
228
    stack.push(entry);
229
    while(stack.length > 0) {
230
        entry = stack.pop();
231
        var start = entry[0];
232
        var end = entry[1];
233
        var depth = entry[2];
234
        if(depth == 0) {
235
         ary = shell_sort_bound(ary,start,end);
236
         continue;
237
        }
238
        depth--;
239
        var pivot = Math.round((start + end) / 2);
240
            
241
        var pivotNewIndex = inplace_quicksort_partition(ary,start,end, pivot);
242
        if(end - pivotNewIndex > 16) {
243
            entry = [pivotNewIndex,end,depth];
244
            stack.push(entry);
245
        }
246
        if(pivotNewIndex - start > 16) {
247
            entry = [start,pivotNewIndex,depth];
248
            stack.push(entry);
249
        }
250
    }
251
    ary = insertion_sort(ary);
252
    return ary;
253
}
254
function shell_sort_bound(ary,start,end) {
255
      var inc = Math.round((start + end)/2),
256
          i, j, t;
257
  
258
      while (inc >= start) {
259
          for (i = inc; i < end; i++) {
260
              t = ary[i];
261
              j = i;
262
              while (j >= inc && ary[j - inc] > t) {
263
                  ary[j] = ary[j - inc];
264
                  j -= inc;
265
              }
266
              ary[j] = t;
267
          }
268
          inc = Math.round(inc / 2.2);
269
      }
270
  
271
      return ary;
272
  }
273
274
function mSort(list) {
275
                if (list.length < 14) {
276
                    return insertion_sort(list);
277
                }
278
                
279
                var half = Math.floor(list.length / 2);
280
                var a = mSort(list.slice(0, half));
281
                var b = mSort(list.slice(half, list.length));
282
                var aCount = 0;
283
                var bCount = 0;
284
                var returnList = [];
285
                
286
                while (true) {
287
                    if (a[aCount] <= b[bCount]) {
288
                        returnList.push(a[aCount]);
289
                        aCount++;
290
                        if (aCount === a.length) {
291
                            returnList = returnList.concat(b.slice(bCount, b.length));
292
                            break;
293
                        }
294
                    }
295
                    else {
296
                        returnList.push(b[bCount]);
297
                        bCount++;
298
                        if (bCount === b.length) {
299
                            returnList = returnList.concat(a.slice(aCount, a.length));
300
                            break;
301
                        }
302
                    }
303
                }
304
                return returnList;
305
            }
306
  
307
  function bubbleSort(items) {
308
  var length = items.length;
309
  for (var i = 0; i < length; i++) { //Number of passes
310
    for (var j = 0; j < (length - i - 1); j++) { //Notice that j < (length - i)
311
      //Compare the adjacent positions
312
      if(items[j] > items[j+1]) {
313
        //Swap the numbers
314
        var tmp = items[j];  //Temporary variable to hold the current number
315
        items[j] = items[j+1]; //Replace current number with adjacent number
316
        items[j+1] = tmp; //Replace adjacent number with current number
317
      }
318
    }        
319
  }
320
}
321
  
322
  //https://stackoverflow.com/a/38979903/8769328
323
  function radixBucketSort (arr) {
324
    var idx1, idx2, idx3, len1, len2, radix, radixKey;
325
    var radices = {}, buckets = {}, num, curr;
326
    var currLen, radixStr, currBucket;
327
328
    len1 = arr.length;
329
    len2 = 10;  // radix sort uses ten buckets
330
331
    // find the relevant radices to process for efficiency        
332
    for (idx1 = 0;idx1 < len1;idx1++) {
333
      radices[arr[idx1].toString().length] = 0;
334
    }
335
336
    // loop for each radix. For each radix we put all the items
337
    // in buckets, and then pull them out of the buckets.
338
    for (radix in radices) {          
339
      // put each array item in a bucket based on its radix value
340
      len1 = arr.length;
341
      for (idx1 = 0;idx1 < len1;idx1++) {
342
        curr = arr[idx1];
343
        // item length is used to find its current radix value
344
        currLen = curr.toString().length;
345
        // only put the item in a radix bucket if the item
346
        // key is as long as the radix
347
        if (currLen >= radix) {
348
          // radix starts from beginning of key, so need to
349
          // adjust to get redix values from start of stringified key
350
          radixKey = curr.toString()[currLen - radix];
351
          // create the bucket if it does not already exist
352
          if (!buckets.hasOwnProperty(radixKey)) {
353
            buckets[radixKey] = [];
354
          }
355
          // put the array value in the bucket
356
          buckets[radixKey].push(curr);          
357
        } else {
358
          if (!buckets.hasOwnProperty('0')) {
359
            buckets['0'] = [];
360
          }
361
          buckets['0'].push(curr);          
362
        }
363
      }
364
      // for current radix, items are in buckets, now put them
365
      // back in the array based on their buckets
366
      // this index moves us through the array as we insert items
367
      idx1 = 0;
368
      // go through all the buckets
369
      for (idx2 = 0;idx2 < len2;idx2++) {
370
        // only process buckets with items
371
        if (buckets[idx2] != null) {
372
          currBucket = buckets[idx2];
373
          // insert all bucket items into array
374
          len1 = currBucket.length;
375
          for (idx3 = 0;idx3 < len1;idx3++) {
376
            arr[idx1++] = currBucket[idx3];
377
          }
378
        }
379
      }
380
      buckets = {};
381
    }
382
  }
383
  
384
  function quickInsertionSort(arr) {
385
  if (!arr || arr.length < 1) {
386
    return;
387
  }
388
  const stack = [];
389
  stack.push([0, arr.length, 2 * Math.floor(Math.log(arr.length) / Math.log(2))]);
390
391
  while (stack.length > 0) {
392
    const [start, end, depth] = stack[stack.length - 1];
393
    stack.pop();
394
395
    if (depth === 0) {
396
      shellSortBound(arr, start, end);
397
    } else {
398
      const pivot = Math.round((start + end) / 2);
399
      const pivotNewIndex = inplaceQuicksortPartition(arr, start, end, pivot);
400
      if (end - pivotNewIndex > 16) {
401
        stack.push([pivotNewIndex, end, depth - 1]);
402
      }
403
      if (pivotNewIndex - start > 16) {
404
        stack.push([start, pivotNewIndex, depth - 1]);
405
      }
406
    }
407
  }
408
  insertionSort(arr);
409
}
410
411
/**
412
 *
413
 * @param {number[]} arr
414
 * @param {number} start
415
 * @param {number} end
416
 */
417
function shellSortBound(arr, start, end) {
418
  let inc = Math.round((start + end) / 2);
419
  let i;
420
  let j;
421
  let t;
422
423
  while (inc >= start) {
424
    for (i = inc; i < end; i++) {
425
      t = arr[i];
426
      j = i;
427
      while (j >= inc && arr[j - inc] > t) {
428
        arr[j] = arr[j - inc];
429
        j -= inc;
430
      }
431
      arr[j] = t;
432
    }
433
    inc = Math.round(inc / 2.2);
434
  }
435
}
436
437
/**
438
 * Insertion sort
439
 * @param {number[]} arr
440
 */
441
function insertionSort(arr) {
442
  for (let i = 1, l = arr.length; i < l; i++) {
443
    const value = arr[i];
444
    let j;
445
    for (j = i - 1; j >= 0; j--) {
446
      if (arr[j] <= value) break;
447
      arr[j + 1] = arr[j];
448
    }
449
    arr[j + 1] = value;
450
  }
451
}
452
453
/**
454
 * In-place quick sort
455
 * @param {number[]} arr
456
 * @param {number} start
457
 * @param {number} end
458
 * @param {number} pivotIndex
459
 * @returns number
460
 */
461
function inplaceQuicksortPartition(arr, start, end, pivotIndex) {
462
  let i = start;
463
  let j = end;
464
  const pivot = arr[pivotIndex];
465
  const partition = true;
466
  while (partition) {
467
    while (arr[i] < pivot) {
468
      i++;
469
    }
470
    j--;
471
    while (pivot < arr[j]) {
472
      j--;
473
    }
474
    if (!(i < j)) {
475
      return i;
476
    }
477
    // swap(arr, i, j);
478
    const t = arr[i];
479
    arr[i] = arr[j];
480
    arr[j] = t;
481
    i++;
482
  }
483
  return i;
484
}
485
  
486
  function quickInsertionSort2(arr) {
487
  if (!arr || arr.length < 1) {
488
    return;
489
  }
490
  let startIndex = 0;
491
  let endIndex = arr.length - 1;
492
  let stackLength = 0;
493
  const startIndexes = [];
494
  const endIndexes = [];
495
  // variables for partitioning
496
  let _swap_temp;
497
  let left;
498
  let partitionIndex;
499
  let pivot;
500
  let right;
501
  // variables for insertion sort
502
  let i;
503
  let j;
504
  let key;
505
  do {
506
    // in my testing, I found 32 is very good choice for totally generated-random data,
507
    // more than 100 will cause slower speed overal.
508
    if (endIndex - startIndex <= 32) {
509
      // even using insertionSort,
510
      // still need this because it still come here !!
511
      if (endIndex - startIndex === 1) {
512
        if (arr[startIndex] > arr[endIndex]) {
513
          _swap_temp = arr[startIndex];
514
          arr[startIndex] = arr[endIndex];
515
          arr[endIndex] = _swap_temp;
516
        }
517
      } else {
518
        // start of insertion sort
519
        for (i = startIndex + 1; endIndex >= i; i++) {
520
          key = arr[i];
521
          // Move elements of arr[startIndex..i-1], that are
522
          // greater than key, to one position ahead
523
          // of their current position
524
          for (j = i - 1; j >= startIndex; j--) {
525
            if (arr[j] > key) {
526
              arr[j + 1] = arr[j];
527
              // eslint-disable-next-line no-continue
528
              continue;
529
            }
530
            // use 'break' to avoid decreasing 'j'
531
            break;
532
          }
533
          // swap
534
          arr[j + 1] = key;
535
        }
536
        // end of insertion sort
537
      }
538
      // continue to process next data, is there any data inside stack ?
539
      if (stackLength > 0) {
540
        // pop
541
        stackLength--; // reduce counter to get the last position from stack
542
        startIndex = startIndexes[stackLength];
543
        endIndex = endIndexes[stackLength];
544
      } else {
545
        // no data inside stack, so finish
546
        break;
547
      }
548
    } else {
549
      // squeeze every millisecond by put main logic here instead of separate function
550
      // start of partitioning
551
      // minimize worst case scenario
552
      // === start of select pivot ============
553
      pivot = arr[startIndex];
554
      // try to find a different element value
555
      j = endIndex;
556
      while (pivot === arr[j] && j >= startIndex) {
557
        j--;
558
      }
559
      if (j > startIndex) {
560
        // check which element is lower?
561
        // use the lower value as pivot
562
        if (pivot > arr[j]) {
563
          pivot = arr[j];
564
        }
565
      }
566
      // === end of select pivot ============
567
      left = startIndex;
568
      right = endIndex;
569
      do {
570
        while (pivot > arr[left]) {
571
          left++;
572
        }
573
        while (arr[right] > pivot) {
574
          right--;
575
        }
576
        if (left >= right) {
577
          partitionIndex = right;
578
          break;
579
        }
580
        // swap(left, right);
581
        // because many swaps, so optimize to implement swap here !
582
        _swap_temp = arr[left];
583
        arr[left] = arr[right];
584
        arr[right] = _swap_temp;
585
        left++;
586
        right--;
587
        // eslint-disable-next-line no-constant-condition
588
      } while (true); // loop forever until break
589
      if (partitionIndex > startIndex) {
590
        // has lower partition, so process it
591
        if (endIndex > partitionIndex + 1) {
592
          // push 'right' side partition info into stack for later
593
          startIndexes[stackLength] = partitionIndex + 1;
594
          endIndexes[stackLength] = endIndex;
595
          stackLength++; // increase counter for NEXT slot
596
        }
597
        // prepare next loop
598
        // keep same value for startIndex but update endIndex
599
        endIndex = partitionIndex;
600
      } else if (endIndex > partitionIndex + 1) {
601
        // at this point, it means there is no 'lower' side partition but has 'higher' side partition
602
        // prepare next loop
603
        // keep same value for endIndex but update startIndex
604
        startIndex = partitionIndex + 1;
605
      }
606
      // end of Tony Hoare partitioning
607
    }
608
  } while (endIndex > startIndex);
609
}
610
</script>
Script Preparation code:
 
var input = [];
for (var i = 0; i < 1000000; i++) {
  input[i] = Math.round(Math.random() * 1000000);
}
Tests:
  • Built-in Sort

     
    builtin_sort(input.slice(0));
  • Naive Quicksort

     
    naive_quicksort(input.slice(0));
  • Inplace Quicksort

     
    inplace_quicksort(input.slice(0), 0, input.length);
  • Heap Sort

     
    heapSort(input.slice(0));
  • Merge Sort

     
    merge_sort(input.slice(0));
  • Shell Sort

     
    shell_sort(input.slice(0));
  • Comb Sort

     
    comb_sort(input.slice(0));
  • Insertion Sort

     
    insertion_sort(input.slice(0));
  • Fast QuickSort

     
    fast_quicksort(input.slice(0));
  • mSort

     
    mSort(input.slice(0));
  • Radix Bucket Sort

     
    radixBucketSort(input.slice(0)) 
  • Bubble Sort

     
    bubbleSort(input.slice(0));
  • Quick Insertion Sort

     
    quickInsertionSort(input.slice(0));
  • QuickInsertionSort

     
    quickInsertionSort2(input.slice(0));
Rendered benchmark preparation results:

Suite status: <idle, ready to run>

Previous results

Experimental features:

  • Test case name Result
    Built-in Sort
    Naive Quicksort
    Inplace Quicksort
    Heap Sort
    Merge Sort
    Shell Sort
    Comb Sort
    Insertion Sort
    Fast QuickSort
    mSort
    Radix Bucket Sort
    Bubble Sort
    Quick Insertion Sort
    QuickInsertionSort

    Fastest: N/A

    Slowest: N/A

Latest run results:
Run details: (Test run date: 3 years ago)
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.61 Safari/537.36
Chrome 94 on Mac OS X 10.15.7
View result in a separate tab
Test name Executions per second
Built-in Sort 8195.7 Ops/sec
Naive Quicksort 4982.1 Ops/sec
Inplace Quicksort 59092.8 Ops/sec
Heap Sort 30375.6 Ops/sec
Merge Sort 9890.0 Ops/sec
Shell Sort 13261.5 Ops/sec
Comb Sort 13544.7 Ops/sec
Insertion Sort 4548.0 Ops/sec
Fast QuickSort 83284.0 Ops/sec
mSort 17901.9 Ops/sec
Radix Bucket Sort 7690.7 Ops/sec
Bubble Sort 1477.4 Ops/sec
Quick Insertion Sort 76584.8 Ops/sec
QuickInsertionSort 76139.8 Ops/sec