原生JavaScriptsorting执行比实施mergesort和quicksort慢

我已经实现了一个mergesort和一个quicksort来比较它们与原生JavaScriptsorting。 对于quicksort我试图使用这个algorithm: 在YouTube上查看algorithm 。 两种algorithm都使用尽可能less的内存,对于合并sorting,为每个recursion调用传递一个辅助数组(以避免开销),并为快速sorting开始和结束位置的位置。 我正在使用sorting来pipe理NodeJs应用程序中的大量数据。

下面你有mergesort,quicksort和本地JavaScriptsorting,你可以testing性能

问题是:为什么本机JavaScriptperformance较慢?

在我的情况下:

Chrome – 合并sorting:测量:1997.920ms; 快速sorting:测量:1755.740ms; 原产地:措施:4988.105ms
节点:合并sorting:测量:2233.413ms; 快速sorting:测量:1876.055ms; 本机:测量:6317.118ms

合并sorting

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) { for (var k = lo; k <= hi; k++) { aux[k] = arr[k]; } var i = lo; var j = mid + 1; for (var k = lo; k <= hi; k++) { if (i > mid) { arr[k] = aux[j++]; } else if (j > hi) { arr[k] = aux[i++]; } else if (aux[i] < aux[j]) { arr[k] = aux[i++]; } else { arr[k] = aux[j++]; } } } function sort(array, aux, lo, hi) { if (hi <= lo) return; var mid = Math.floor(lo + (hi - lo) / 2); sort(array, aux, lo, mid); sort(array, aux, mid + 1, hi); merge(array, aux, lo, mid, hi); } function merge_sort(array) { var aux = array.slice(0); sort(array, aux, 0, array.length - 1); return array; } return merge_sort(array); } console.time('measure'); mergeSort(arr); console.timeEnd('measure'); console.log(arr[0], arr[1]); 

快速sorting

 var length = 10000000; // ten millions; var arr = []; for (let i = length; i > 0; i--) { // random array arr.push(parseInt(Math.random() * 1000000000)); } function quickSort(arr, leftPos, rightPos, arrLength) { let initialLeftPos = leftPos; let initialRightPos = rightPos; let direction = true; let pivot = rightPos; while ((leftPos - rightPos) < 0) { if (direction) { if (arr[pivot] < arr[leftPos]) { quickSort.swap(arr, pivot, leftPos); pivot = leftPos; rightPos--; direction = !direction; } else leftPos++; } else { if (arr[pivot] <= arr[rightPos]) { rightPos--; } else { quickSort.swap(arr, pivot, rightPos); leftPos++; pivot = rightPos; direction = !direction; } } } if (pivot - 1 > initialLeftPos) { quickSort(arr, initialLeftPos, pivot - 1, arrLength); } if (pivot + 1 < initialRightPos) { quickSort(arr, pivot + 1, initialRightPos, arrLength); } } quickSort.swap = (arr, el1, el2) => { let swapedElem = arr[el1]; arr[el1] = arr[el2]; arr[el2] = swapedElem; } arrLength = arr.length; console.time('measure'); quickSort(arr, 0, arrLength - 1, arrLength); console.log(arr[0], arr[1]); console.timeEnd('measure'); 

本地的JavaScriptsorting

 var length = 10000000; // ten millions; var arr = []; for (let i = length; i > 0; i--) { // random array arr.push(parseInt(Math.random() * 100000000)); } console.time('measure'); arr.sort(function compareNumbers(a, b) { return a - b; }); console.timeEnd('measure'); console.log(arr[0], arr[1]); 

那么为什么本地sorting较慢? 在看代码

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

这个问题似乎是GetThirdIndex()。 它在分区大小大于1000时被调用,我假设它用来防止快速sorting最坏情况下的性能,但是开销很大,因为它创build了内部对的数组并对它们进行sorting,并且这些对的sorting可能导致进一步的recursion调用GetThirdIndex()。 这与recursion调用相结合,分割原始数组和分割内部数组对。

由于这些示例的testing数据是随机数据,因此Relu的快速sorting不需要GetThirdIndex()之类的东西。 还有arrays中的“洞”检查,但我认为这并不重要。

GetThirdIndex()的一个替代方法是中位数的中位数:

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

合并sorting比快速sorting快,这些方法用于防止最坏的情况,但是它需要一个与原始数组大小相同或一半大小的辅助数组。

Introsort是快速sorting和堆sorting的混合体,如果recursion的级别太深,就会转换到堆​​sorting。

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

下面的第二个合并sorting示例使用了一个比较函数来进行公平比较。 这比原生版本快得多。 在Chrome的情况下,比较function不会影响整体时间。 在Firefox的情况下,比较function有更多的效果。 在Firefox的情况下,本地版本的内存不足,所以我无法比较。

这些是自上而下的合并sorting的一些更快的版本,原始的海报是“好奇的”,使用相互recursion函数来避免复制和有点优化merge()(每比较两个条件)。

Firefox的结果(时间有所不同)

 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 

Chrome的结果(时间有所不同)

 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 

合并sorting

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