为啥数字数组,更多数据排序比对象数组更快,Javascript中的数据更少?

Posted

技术标签:

【中文标题】为啥数字数组,更多数据排序比对象数组更快,Javascript中的数据更少?【英文标题】:Why do arrays of numbers, more data sort faster than arrays of objects, fewer data in Javascript?为什么数字数组,更多数据排序比对象数组更快,Javascript中的数据更少? 【发布时间】:2016-04-01 07:48:39 【问题描述】:

对于我在 node.js 中的应用程序,我必须根据某个数值(即数字等级)按降序对数组元素进行排序。由于我的应用程序对性能至关重要,因此我决定构建我的数据结构以优化排序。我假设数组中每个元素包含的数据越少,排序就越快。为了验证我的假设,我在三个长度为 10000 的不同数组上运行了以下命令:

编辑:伙计们,我最初的测试似乎有问题。第一个测试比接下来的测试花费的时间要长得多。因此,我修改了我的测试代码,在实际排序之前有一个“缓冲区”排序。此外,我为固定数量的试验轮换了测试的顺序,以减少测试本身的顺序可能导致的任何偏差。我已经相应地修改了结果。

完整来源:https://raw.githubusercontent.com/youngrrrr/js-array-sort-bench-test/master/arraySortTest.js

var buffer = [781197, ... ];
var sparseArray = [781197, ... ];
var sparseArray2 = ['a' : 781197, ...];
var denseArray = ['a' : 781197, 'b': ['r', 'a', 'n', 'd', 'o', 'm'] , ...];

/* buffer : for some reason, the first test always takes significantly longer than the others. I've added this to try to remove whatever bias there was before... */
console.time('buffer');
random.sort(compareSparse);
console.timeEnd('buffer');
console.log(buffer[0]); // prints "58"


/* sparseArray : an array whose elements are numbers */
console.time('sparse');
sparseArray.sort(compareSparse);
console.timeEnd('sparse');
console.log(sparseArray[0]); // prints "58"

/* sparseArray2 (not an accurate name, just got lazy) :
   an array whose elements are objects with a single key-value pair mapping
   an arbitrary name 'a' to a number (which we sort on) */
console.time('sparse2');
sparseArray2.sort(compareDense);
console.timeEnd('sparse2');
console.log(sparseArray2[0]); // prints " a: 58 "

/* denseArray : an array whose elements are objects with two key-value
   pairs mapping an arbitrary key 'a' to a number (which we sort on) and
   another arbitrary key 'b' to an array (which is just supposed to be 
   extra data for the purpose of my hypothesis) */
console.time('dense');
denseArray.sort(compareDense);
console.timeEnd('dense');
console.log(denseArray[0]); // prints " a: 58, b: [ 'r', 'a', 'n', 'd', 'o', 'm' ] "

function compareSparse(a, b) 
    if (a < b) 
        return -1;
     else if (a > b) 
        return 1;   
    else 
        return 0;
    


function compareDense(a, b) 
    if (a.a < b.a) 
            return -1;
         else if (a.a > b.a) 
            return 1;   
        else 
            return 0;
        
    

旧测试:

经过 25 次试验(我知道,样本量很小,但我都是手动完成的)我得到以下平均排序时间:

sparseArray:(24 + 23 + 21 + 23 + 21 + 22 + 22 + 22 + 22 + 22 + 21 + 20 + 22 + 24 + 24 + 21 + 22 + 22 + 25 + 23 + 24 + 23 + 21 + 21 + 23) / 25 = 22.32ms sparseArray2:(4 + 4 + 4 + 4 + 4 + 5 + 5 + 5 + 5 + 4 + 6 + 5 + 5 + 4 + 5 + 4 + 4 + 4 + 5 + 6 + 4 + 5 + 4 + 4 + 5) / 25 = 4.56ms 密集阵列:(5 + 5 + 4 + 5 + 5 + 5 + 5 + 5 + 5 + 6 + 5 + 5 + 4 + 4 + 5 + 5 + 5 + 4 + 5 + 5 + 6 + 5 + 5 + 5 + 4) / 25 = 4.88ms

新测试:

经过 25 次试验(我知道,样本量很小,但我都是手动完成的)我得到以下平均排序时间:

sparseArray: (4+4+4+4+3+4+4+4+4+4+4+4+3+4+4)/15 = 3.867ms强> sparseArray2: (4+4+4+6+5+4+4+4+4+5+5+4+5+5+5)/15 = 4.533ms强> denseArray:(4+4+4+5+5+4+4+4+4+5+5+4+5+5+5)/15 = 4.466ms强>

所以我得出以下结论:

数字数组的排序比值是数字的对象数组快。这在直觉上是有道理的。 出于某种原因,自相矛盾的是,特定元素中的数据越多,排序速度越快(sparseArray2 与 denseArray 运行时相比)。

我想知道的是:

除了我的测试之外,这些结论是否有任何文档/其他内容的支持?也就是说,我得出了正确的结论吗? 为什么?为什么数字数组的排序比对象数组快(直觉上是有道理的,但是这背后的解释是什么,如果有的话)?不仅如此,为什么包含 MORE 数据的数组似乎比包含 less 数据的数组排序更快?

请注意,我不接受这些结论或任何事情。样本量很小,而且我的测试以前被证明是有缺陷的,所以我的结果很可能只是糟糕的测试的结果。此外,似乎有各种我不知道的因素可能会影响结果(正如 Ryan O'Hara 在我之前的帖子中指出的那样)。这篇文章的重点是发现任何基于事实的 javascript 排序行为解释。

感谢阅读!

【问题讨论】:

重新排序测试,你会得到截然不同的答案——最好是迭代这些。 上次我检查(大约一个月前)V8 没有检测到纯函数并内联它们,所以性能最高的方法可能是根本不使用本机排序的方法.. .无论如何我仍然认为这可能是过早的优化:) “本地排序”是指[].sort 方法,因为您必须多次调用一个函数。使用循环可能会更快(同样,鉴于目前的编译器优化,未来可能会逆转) 技术上值得一提的是没有“javascript”排序性能。不同的实现会执行不同的操作,因为 ECMAScript 标准只指定了操作的结果,而不是如何去做。实际上,这意味着性能可以在给定实现的版本之间发生变化,并且仍然遵守标准 我手头没有它,但我知道它是由开发人员明确声明为几个月前最新的。只是谷歌纯函数内联...... 【参考方案1】:

这些结论是否有任何文档/其他内容的支持? 我的测试?也就是说,我得出了正确的结论吗?

.sort() 如何实现的细节不是任何规范都要求的,因此.sort() 的性能方面只能通过在浏览器或感兴趣的 JS 实现中测试有趣的数据集来发现。几乎所有性能问题都最好通过在对您而言重要的特定情况下进行测试来回答。超出此范围的概括很容易产生误导或错误,并且不一定适用于所有配置。

为什么?为什么数字数组排序比对象数组快 (直觉上是有道理的,但是这背后的解释是什么,如果 任何)?不仅如此,为什么包含更多数据的数组似乎 比那些包含较少数据的排序更快?

具有自定义比较功能的给定排序的性能将由以下项目控制:

    数组的长度。更长的数组需要更多的排序比较。 内部排序算法的巧妙之处在于尽可能减少排序比较次数 自定义排序功能的性能(执行给定排序比较需要多长时间)。

因此,如果您持有自定义排序函数和 .sort() 实现,您正在使用常量和数组常量中的数据,那么更长的数组将需要更长的时间来排序。

但是,如果您同时更改上面的 1. 和 3.(一个在有利的方向,一个在不太有利的方向),就像您从对数字数组排序到按 a 对对象数组排序时所做的那样特定的属性值,那么速度的增量将取决于净变化是正面的还是负面的,这取决于在非常具体的实现和数据集以及大量测试之外难以预测的几件事(换句话说,它可以去任何一种方式)。

有关对数字数组进行排序与​​从对象数组中对属性进行排序的一些测试信息,请参阅http://jsperf.com/sort-value-vs-property。毫不奇怪,对数字数组进行排序会稍微快一些。

【讨论】:

很好的讨论!不过需要注意的是:所有测试数组的长度都相同:每个数组有 10,000 个元素。由于排序键在每个数组中看起来都是相同的,因此它们都应该导致对排序函数的相同数量的调用 - 实际上,调用和参数值的 sequence 相同。排序函数之间的一个明显区别是其中一个函数的工作量更大,因为它使用属性引用而不是直接数值。 @MichaelGeary - 这就是重点 - “科学方法”。我正在尝试比较等长的值和对象数组,看看哪个实际上更快。在更改另一个变量时保持一个变量不变,以便一次测试一件事。如果您将两个不同长度的数组与不同的内容进行比较,那么结果不会告诉您任何有用的信息,因为您不知道哪个更改导致了结果的差异。是的,结果如您所愿,但测试告诉您,即使对于相当长的数组,差异也不是很大。 这是我所指的具体错误:“如果您同时更改上面的 1. 和 3.(一个在有利的方向,一个在不太有利的方向),就像你从对数字数组进行排序以按特定属性值对对象数组进行排序”只有您的第 3 点在排序示例之间发生变化;点 1(数组的长度)在所有这些中都是相同的。 @MichaelGeary - 如果您阅读了我的回答,我在解释您不能通过同时更改两件事来得出任何合理的测试结论。当它显示得更慢或更快时,您将不知道为什么。您必须一次更改一件事并进行衡量。我们都知道,如果其他所有条件都相同,则更长的数组排序会更慢。因此,剩下的唯一问题是排序值与属性之间有多大差异。这就是我展示的。如果您想更改两件事,那么您的结果仅对 OP 未提供的一组特定数据有意义。 @MichaelGeary - 如果您测试同时更改两件事,您无法得出任何一般性结论 - 只有针对该确切数据集的特定结论。【参考方案2】:

我认为这与 javascript 中的排序方式有关。数字are converted to strings如果未提供比较函数,则排序前需要一些时间。

【讨论】:

这是默认比较功能。 OP 使用自定义的。 我认为这是一个公平的观点(我一定会牢记这一点!)但正如其他人所指出的,由于我使用了自定义比较功能,这并不适用。

以上是关于为啥数字数组,更多数据排序比对象数组更快,Javascript中的数据更少?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用数组比使用地图记忆更快?

为啥聚合+排序比mongo中的查找+排序更快?

为啥向后迭代数组比向前迭代更快

为啥 Numpy 和 Pandas 数组比源数据消耗更多内存? [关闭]

为啥使用循环从数组的开始迭代到结束比迭代开始到结束和结束到开始更快?

为啥插入排序比快速排序和冒泡排序更快?