So why is the native sort slower? Looking at the code in

https://github.com/v8/v8/blob/0c76b0ae850027006d5ec0d92449e449d996d3bb/src/js/array.js#L744

The issue seems to be GetThirdIndex(). It gets called when partition size is > 1000, and I assume it's used to prevent quicksort worst case performance, but the overhead is significant, as it creates internal arrays of pairs and sorts those, and the sorting of those pairs can result in further recursive calls to GetThirdIndex(). This is combined with the recursive calls related to partitioning the original array and to partitioning the internal arrays of pairs.

Since the test data for these examples is random data, Relu's quicksort doesn't need something like GetThirdIndex(). There's also the check for "holes" in the array, but I assume this isn't significant.

An alternative to GetThirdIndex() would be an in place median of medians:

http://en.wikipedia.org/wiki/Median_of_medians

Merge sort is faster than quicksort with these methods used to prevent worst case scenarios, but it needs an auxiliary array the same size or half the size of the original array.

Introsort, which is a hybrid of quicksort and heapsort, switching to heapsort if the level of recursion gets too deep would be an alternative:

http://en.wikipedia.org/wiki/Introsort

The second merge sort example below uses a compare function, to make a fair comparison. It's significantly faster than the native version. In the case of Chrome, a compare function didn't affect the overall time much. In the case of Firefox, the compare function had more effect. In the case of Firefox, the native version failed for out of memory so I couldn't compare that.

These are somewhat faster versions of top down merge sort which the original poster was "curious" about, using mutually recursive functions to avoid copy and somewhat optimized merge() (two conditionals per compare).

Results with Firefox (times vary somewhat)

native sort - failed for out of memory. Relu's merge sort - 1.8 seconds Relu's quick sort - 1.3 seconds optimized merge sort - 1.4 seconds optimized merge sort with compare - 1.8 seconds

Results with Chrome (times vary somewhat)

native sort - 5.3 seconds Relu's merge sort - 2.1 seconds Relu's quick sort - 1.8 seconds optimized merge sort - 1.6 seconds optimized merge sort with compare - 1.7 seconds

Merge Sort

var length = 10000000; // ten millions; var arr = []; for (let i = length; i > 0; i--) { // random array arr.push(parseInt(Math.random() * 1000000000)); } var mergeSort = function(array) { function merge(arr, aux, lo, mid, hi) { var i = lo; var j = mid + 1; var k = lo; while(true){ if(arr[i] <= arr[j]){ aux[k++] = arr[i++]; if(i > mid){ do aux[k++] = arr[j++]; while(j <= hi); break; } } else { aux[k++] = arr[j++]; if(j > hi){ do aux[k++] = arr[i++]; while(i <= mid); break; } } } } function sortarrtoaux(arr, aux, lo, hi) { if (hi < lo) return; if (hi == lo){ aux[lo] = arr[lo]; return; } var mid = Math.floor(lo + (hi - lo) / 2); sortarrtoarr(arr, aux, lo, mid); sortarrtoarr(arr, aux, mid + 1, hi); merge(arr, aux, lo, mid, hi); } function sortarrtoarr(arr, aux, lo, hi) { if (hi <= lo) return; var mid = Math.floor(lo + (hi - lo) / 2); sortarrtoaux(arr, aux, lo, mid); sortarrtoaux(arr, aux, mid + 1, hi); merge(aux, arr, lo, mid, hi); } function merge_sort(arr) { var aux = arr.slice(0); sortarrtoarr(arr, aux, 0, arr.length - 1); return arr; } return merge_sort(array); } console.time('measure'); mergeSort(arr); console.timeEnd('measure'); console.log(arr[0], arr[1]);

Merge Sort with compare function

var length = 10000000; // ten millions; var arr = []; for (let i = length; i > 0; i--) { // random array arr.push(parseInt(Math.random() * 1000000000)); } var mergeSort = function(array, comparefn) { function merge(arr, aux, lo, mid, hi, comparefn) { var i = lo; var j = mid + 1; var k = lo; while(true){ var cmp = comparefn(arr[i], arr[j]); if(cmp <= 0){ aux[k++] = arr[i++]; if(i > mid){ do aux[k++] = arr[j++]; while(j <= hi); break; } } else { aux[k++] = arr[j++]; if(j > hi){ do aux[k++] = arr[i++]; while(i <= mid); break; } } } } function sortarrtoaux(arr, aux, lo, hi, comparefn) { if (hi < lo) return; if (hi == lo){ aux[lo] = arr[lo]; return; } var mid = Math.floor(lo + (hi - lo) / 2); sortarrtoarr(arr, aux, lo, mid, comparefn); sortarrtoarr(arr, aux, mid + 1, hi, comparefn); merge(arr, aux, lo, mid, hi, comparefn); } function sortarrtoarr(arr, aux, lo, hi, comparefn) { if (hi <= lo) return; var mid = Math.floor(lo + (hi - lo) / 2); sortarrtoaux(arr, aux, lo, mid, comparefn); sortarrtoaux(arr, aux, mid + 1, hi, comparefn); merge(aux, arr, lo, mid, hi, comparefn); } function merge_sort(arr, comparefn) { var aux = arr.slice(0); sortarrtoarr(arr, aux, 0, arr.length - 1, comparefn); return arr; } return merge_sort(array, comparefn); } console.time('measure'); mergeSort(arr, function compareNumbers(a, b) { return a - b; }); console.timeEnd('measure'); // check result for (let i = 1; i < length; i++) { if(arr[i] < arr[i-1]){ console.log('error'); break; } } console.log(arr[0], arr[1]);

Side note: native sort is not stable:

Native Javascript Sort - test for stability

var length = 100000; var arr = []; var j; for (let i = 0; i < length; i++) { j = parseInt(Math.random() * 100); arr[i] = [j, i]; } console.time('measure'); arr.sort(function compareNumbers(a, b) { return a[0] - b[0]; }); console.timeEnd('measure'); for (let i = 1; i < length; i++) { if( (arr[i][0] == arr[i-1][0]) && (arr[i][1] < arr[i-1][1]) ){ console.log('not stable'); console.log(arr[i-1][0], arr[i-1][1]); console.log(arr[i ][0], arr[i ][1]); break; } }

Native Javascript Sort - change compare to make it stable