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
               
225
//faster quicksort using a stack to eliminate recursion, sorting inplace to reduce memory usage, and using insertion sort for small partition sizes
226
function fast_quicksort(ary) {
227
    var stack = [];
228
    var entry = [0,ary.length,2 * Math.floor(Math.log(ary.length)/Math.log(2))];
229
    stack.push(entry);
230
    while(stack.length > 0) {
231
        entry = stack.pop();
232
        var start = entry[0];
233
        var end = entry[1];
234
        var depth = entry[2];
235
        if(depth == 0) {
236
         ary = shell_sort_bound(ary,start,end);
237
         continue;
238
        }
239
        depth--;
240
        var pivot = Math.round((start + end) / 2);
241
            
242
        var pivotNewIndex = inplace_quicksort_partition(ary,start,end, pivot);
243
        if(end - pivotNewIndex > 16) {
244
            entry = [pivotNewIndex,end,depth];
245
            stack.push(entry);
246
        }
247
        if(pivotNewIndex - start > 16) {
248
            entry = [start,pivotNewIndex,depth];
249
            stack.push(entry);
250
        }
251
    }
252
    ary = insertion_sort(ary);
253
    return ary;
254
}
255
function shell_sort_bound(ary,start,end) {
256
      var inc = Math.round((start + end)/2),
257
          i, j, t;
258
  
259
      while (inc >= start) {
260
          for (i = inc; i < end; i++) {
261
              t = ary[i];
262
              j = i;
263
              while (j >= inc && ary[j - inc] > t) {
264
                  ary[j] = ary[j - inc];
265
                  j -= inc;
266
              }
267
              ary[j] = t;
268
          }
269
          inc = Math.round(inc / 2.2);
270
      }
271
  
272
      return ary;
273
  }
274
275
function mSort(list) {
276
                if (list.length < 14) {
277
                    return insertion_sort(list);
278
                }
279
                
280
                var half = Math.floor(list.length / 2);
281
                var a = mSort(list.slice(0, half));
282
                var b = mSort(list.slice(half, list.length));
283
                var aCount = 0;
284
                var bCount = 0;
285
                var returnList = [];
286
                
287
                while (true) {
288
                    if (a[aCount] <= b[bCount]) {
289
                        returnList.push(a[aCount]);
290
                        aCount++;
291
                        if (aCount === a.length) {
292
                            returnList = returnList.concat(b.slice(bCount, b.length));
293
                            break;
294
                        }
295
                    }
296
                    else {
297
                        returnList.push(b[bCount]);
298
                        bCount++;
299
                        if (bCount === b.length) {
300
                            returnList = returnList.concat(a.slice(aCount, a.length));
301
                            break;
302
                        }
303
                    }
304
                }
305
                return returnList;
306
            }
307
  
308
  function bubbleSort(items) {
309
  var length = items.length;
310
  for (var i = 0; i < length; i++) { //Number of passes
311
    for (var j = 0; j < (length - i - 1); j++) { //Notice that j < (length - i)
312
      //Compare the adjacent positions
313
      if(items[j] > items[j+1]) {
314
        //Swap the numbers
315
        var tmp = items[j];  //Temporary variable to hold the current number
316
        items[j] = items[j+1]; //Replace current number with adjacent number
317
        items[j+1] = tmp; //Replace adjacent number with current number
318
      }
319
    }        
320
  }
321
}
322
  
323
  function quickInsertionSort(arr) {
324
  if (!arr || arr.length < 1) {
325
    return;
326
  }
327
  let startIndex = 0;
328
  let endIndex = arr.length - 1;
329
  let stackLength = 0;
330
  const startIndexes = [];
331
  const endIndexes = [];
332
  // variables for partitioning
333
  let _swap_temp;
334
  let left;
335
  let partitionIndex;
336
  let pivot;
337
  let right;
338
  // variables for insertion sort
339
  let i;
340
  let j;
341
  let key;
342
  do {
343
    // in my testing, I found 32 is very good choice for totally generated-random data,
344
    // more than 100 will cause slower speed overal.
345
    if (endIndex - startIndex <= 32) {
346
      // even using insertionSort,
347
      // still need this because it still come here !!
348
      if (endIndex - startIndex === 1) {
349
        if (arr[startIndex] > arr[endIndex]) {
350
          _swap_temp = arr[startIndex];
351
          arr[startIndex] = arr[endIndex];
352
          arr[endIndex] = _swap_temp;
353
        }
354
      } else {
355
        // start of insertion sort
356
        for (i = startIndex + 1; endIndex >= i; i++) {
357
          key = arr[i];
358
          // Move elements of arr[startIndex..i-1], that are
359
          // greater than key, to one position ahead
360
          // of their current position
361
          for (j = i - 1; j >= startIndex; j--) {
362
            if (arr[j] > key) {
363
              arr[j + 1] = arr[j];
364
              // eslint-disable-next-line no-continue
365
              continue;
366
            }
367
            // use 'break' to avoid decreasing 'j'
368
            break;
369
          }
370
          // swap
371
          arr[j + 1] = key;
372
        }
373
        // end of insertion sort
374
      }
375
      // continue to process next data, is there any data inside stack ?
376
      if (stackLength > 0) {
377
        // pop
378
        stackLength--; // reduce counter to get the last position from stack
379
        startIndex = startIndexes[stackLength];
380
        endIndex = endIndexes[stackLength];
381
      } else {
382
        // no data inside stack, so finish
383
        break;
384
      }
385
    } else {
386
      // squeeze every millisecond by put main logic here instead of separate function
387
      // start of partitioning
388
      // minimize worst case scenario
389
      // === start of select pivot ============
390
      pivot = arr[startIndex];
391
      // try to find a different element value
392
      j = endIndex;
393
      while (pivot === arr[j] && j >= startIndex) {
394
        j--;
395
      }
396
      if (j > startIndex) {
397
        // check which element is lower?
398
        // use the lower value as pivot
399
        if (pivot > arr[j]) {
400
          pivot = arr[j];
401
        }
402
      }
403
      // === end of select pivot ============
404
      left = startIndex;
405
      right = endIndex;
406
      do {
407
        while (pivot > arr[left]) {
408
          left++;
409
        }
410
        while (arr[right] > pivot) {
411
          right--;
412
        }
413
        if (left >= right) {
414
          partitionIndex = right;
415
          break;
416
        }
417
        // swap(left, right);
418
        // because many swaps, so optimize to implement swap here !
419
        _swap_temp = arr[left];
420
        arr[left] = arr[right];
421
        arr[right] = _swap_temp;
422
        left++;
423
        right--;
424
        // eslint-disable-next-line no-constant-condition
425
      } while (true); // loop forever until break
426
      if (partitionIndex > startIndex) {
427
        // has lower partition, so process it
428
        if (endIndex > partitionIndex + 1) {
429
          // push 'right' side partition info into stack for later
430
          startIndexes[stackLength] = partitionIndex + 1;
431
          endIndexes[stackLength] = endIndex;
432
          stackLength++; // increase counter for NEXT slot
433
        }
434
        // prepare next loop
435
        // keep same value for startIndex but update endIndex
436
        endIndex = partitionIndex;
437
      } else if (endIndex > partitionIndex + 1) {
438
        // at this point, it means there is no 'lower' side partition but has 'higher' side partition
439
        // prepare next loop
440
        // keep same value for endIndex but update startIndex
441
        startIndex = partitionIndex + 1;
442
      }
443
      // end of Tony Hoare partitioning
444
    }
445
  } while (endIndex > startIndex);
446
}
447
  
448
  // https://stackoverflow.com/a/38979903/8769328
449
  function radixBucketSort (arr) {
450
    var idx1, idx2, idx3, len1, len2, radix, radixKey;
451
    var radices = {}, buckets = {}, num, curr;
452
    var currLen, radixStr, currBucket;
453
454
    len1 = arr.length;
455
    len2 = 10;  // radix sort uses ten buckets
456
457
    // find the relevant radices to process for efficiency        
458
    for (idx1 = 0;idx1 < len1;idx1++) {
459
      radices[arr[idx1].toString().length] = 0;
460
    }
461
462
    // loop for each radix. For each radix we put all the items
463
    // in buckets, and then pull them out of the buckets.
464
    for (radix in radices) {          
465
      // put each array item in a bucket based on its radix value
466
      len1 = arr.length;
467
      for (idx1 = 0;idx1 < len1;idx1++) {
468
        curr = arr[idx1];
469
        // item length is used to find its current radix value
470
        currLen = curr.toString().length;
471
        // only put the item in a radix bucket if the item
472
        // key is as long as the radix
473
        if (currLen >= radix) {
474
          // radix starts from beginning of key, so need to
475
          // adjust to get redix values from start of stringified key
476
          radixKey = curr.toString()[currLen - radix];
477
          // create the bucket if it does not already exist
478
          if (!buckets.hasOwnProperty(radixKey)) {
479
            buckets[radixKey] = [];
480
          }
481
          // put the array value in the bucket
482
          buckets[radixKey].push(curr);          
483
        } else {
484
          if (!buckets.hasOwnProperty('0')) {
485
            buckets['0'] = [];
486
          }
487
          buckets['0'].push(curr);          
488
        }
489
      }
490
      // for current radix, items are in buckets, now put them
491
      // back in the array based on their buckets
492
      // this index moves us through the array as we insert items
493
      idx1 = 0;
494
      // go through all the buckets
495
      for (idx2 = 0;idx2 < len2;idx2++) {
496
        // only process buckets with items
497
        if (buckets[idx2] != null) {
498
          currBucket = buckets[idx2];
499
          // insert all bucket items into array
500
          len1 = currBucket.length;
501
          for (idx3 = 0;idx3 < len1;idx3++) {
502
            arr[idx1++] = currBucket[idx3];
503
          }
504
        }
505
      }
506
      buckets = {};
507
    }
508
  }
509
</script>
Script Preparation code:
 
var input = [];
for (var i = 0; i < 1000000; i++) {
  input[i] = 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);
  • Fast QuickSort

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

     
    quickInsertionSort(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
    Fast QuickSort
    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/95.0.4638.54 Safari/537.36
Chrome 95 on Mac OS X 10.15.7
View result in a separate tab
Test name Executions per second
Built-in Sort 1.1 Ops/sec
Naive Quicksort 1.5 Ops/sec
Inplace Quicksort 8.4 Ops/sec
Fast QuickSort 10.2 Ops/sec
QuickInsertionSort 11.2 Ops/sec