var timer = function(name) {
var start = new Date();
return {
stop: function() {
var end = new Date();
var time = end.getTime() - start.getTime();
console.log('Timer:', name, 'finished in', time, 'ms');
}
}
};
var getRandom = function(min, max) {
return Math.random() * (max - min) + min;
};
var lastNames = ['SMITH', 'JOHNSON', 'WILLIAMS', 'JONES', 'BROWN', 'DAVIS', 'MILLER', 'WILSON', 'MOORE', 'TAYLOR', 'ANDERSON', 'THOMAS'];
var genLastName = function() {
var index = Math.round(getRandom(0, lastNames.length - 1));
return lastNames[index];
};
var sex = ["Male", "Female"];
var genSex = function() {
var index = Math.round(getRandom(0, sex.length - 1));
return sex[index];
};
var Person = function() {
this.name = genLastName();
this.age = Math.round(getRandom(0, 100))
this.sex = "Male"
};
var genPersons = function() {
for (var i = 0; i < 100000; i++)
personArray.push(new Person());
};
var changeSex = function() {
for (var i = 0; i < personArray.length; i++) {
personArray[i].sex = genSex();
}
};
var deleteMale = function() {
for (var i = 0; i < personArray.length; i++) {
if (personArray[i].sex === "Male") {
personArray.splice(i, 1)
i--
}
}
};
var t = timer("Array");
var personArray = [];
genPersons();
changeSex();
deleteMale();
t.stop();
console.log("Done! There are " + personArray.length + " persons.")
设置代码:
var timer = function(name) {
var start = new Date();
return {
stop: function() {
var end = new Date();
var time = end.getTime() - start.getTime();
console.log('Timer:', name, 'finished in', time, 'ms');
}
}
};
var getRandom = function (min, max) {
return Math.random() * (max - min) + min;
};
var lastNames = ['SMITH','JOHNSON','WILLIAMS','JONES','BROWN','DAVIS','MILLER','WILSON','MOORE','TAYLOR','ANDERSON','THOMAS'];
var genLastName = function() {
var index = Math.round(getRandom(0, lastNames.length - 1));
return lastNames[index];
};
var sex = ["Male", "Female"];
var genSex = function() {
var index = Math.round(getRandom(0, sex.length - 1));
return sex[index];
};
var Person = function() {
this.name = genLastName();
this.age = Math.round(getRandom(0,100))
this.sex = "Male"
};
var genPersons = function() {
for (var i = 0; i < 100000; i++)
personSet.add(new Person());
};
var changeSex = function() {
for (var key of personSet) {
key.sex = genSex();
}
};
var deleteMale = function() {
for (var key of personSet) {
if (key.sex === "Male") {
personSet.delete(key)
}
}
};
var t = timer("Set");
var personSet = new Set();
genPersons();
changeSex();
deleteMale();
t.stop();
console.log("Done! There are " + personSet.size + " persons.")
key in myObject:每秒883055194.54次操作‡ 2.08% ...每秒操作数比myObject.hasOwnProperty高5倍。 更新日期:2022年11月10日:今天我在Safari和Chrome上重新运行了同样的测试(在我的原始图像2年后),得到了一些有趣的结果:TLDR Set在两种浏览器上都比key in Object快,甚至快得多。Chrome还在某种程度上显著优化了Array.includes,使其与Object/Set查找时间处于相同的速度范围内(而for循环要多花1000+倍的时间来完成)。 对于Safari来说,Set明显比key in Object快,而Object. hasOwnProperty几乎不在同一速度范围内。所有数组变体(循环/包含)都比set/object查找慢得多。 2022年11月10日快照:在Safari v16.1上测试的每秒操作数(更高=更快):
共计1 550 924 292.31英镑
key in myObject:942,192,599.63(速度降低39.25%,即使用Set时,每秒可以多执行约1.6倍的操作
在Chrome浏览器上www.example.com 2022年11月10日:v107.0.5304.87它们现在几乎相等了。(虽然预期的行为是set将优于Object,因为set与object的选项可能性更小,以及这是Safari中的行为。)值得注意的是,Array. includes显然在Chrome(v8)中至少针对此类测试进行了显著优化: It is no longer true that Set significantly underperforms Object in operation: they now nearly tie. (Though the expected behavior is that set would outperform Object in due to the smaller possible of options with a set vs an object and how this is the behavior in Safari.) Notably impressive Array.includes has apparently been significantly optimized in Chrome (v8) for at least this type of test:
console.time("set")
var s = new Set()
for(var i = 0; i < 10000; i++)
s.add(Math.random())
s.forEach(function(e){
s.delete(e)
})
console.timeEnd("set")
console.time("array")
var s = new Array()
for(var i = 0; i < 10000; i++)
s.push(Math.random())
s.forEach(function(e,i){
s.splice(i)
})
console.timeEnd("array")
7条答案
按热度按时间k2fxgqgv1#
好的,我已经测试了从数组和集合中添加、迭代和删除元素。我运行了一个“小”测试,使用10000个元素,和一个“大”测试,使用100000个元素。以下是结果。
向集合添加元素
看起来
.push
数组方法比.add
集合方法快大约4倍,无论添加的元素数量是多少。迭代和修改集合中的元素
在这部分测试中,我使用了
for
循环来迭代数组,使用了for of
循环来迭代集合。同样,迭代数组的速度更快了。这一次,它看起来是指数级的,因为在“小”测试中花费了两倍的时间,在“大”测试中几乎花费了四倍的时间。从集合中删除元素
这就是有趣的地方,我使用
for
循环和.splice
的组合从数组中删除一些元素,使用for of
和.delete
从集合中删除一些元素。从集合中删除项目的速度大约是原来的三倍(2.6ms对7.1ms)但是对于其中花费1955.1ms来从阵列中移除项目而仅花费83.6ms来从集合中移除项目的“大”测试来说情况发生了剧烈变化,快了23倍。结论
对于10k个元素,两个测试的运行时间相当(数组:16.6 ms,设置:20.7毫秒),但在处理100k个元素时,集合明显赢家(数组:1974.8毫秒,设置:83.6毫秒),但只是因为删除操作。否则阵列更快。我不能确切地说这是为什么。
我尝试了一些混合场景,其中创建并填充了一个数组,然后将其转换为一个集合,其中删除了一些元素,然后将该集合重新转换为一个数组。尽管这样做会比删除数组中的元素给予更好的性能,但向集合传输和从集合传输所需的额外处理时间超过了填充数组而不是填充集合所带来的好处。最后,只处理一个集合的速度更快。不过,这是一个有趣的想法,如果选择使用数组作为一些没有重复项的大数据的数据集合,如果需要在一个操作中删除许多元素,将数组转换为集合,执行删除操作,然后将集合转换回数组,则这可能会在性能方面具有优势。
数组代码:
设置代码:
irlmq6kh2#
意见:
我分享了一些性能测试。试着打开你的控制台并复制粘贴下面的代码。
正在创建一个数组(125000)
1.查找索引
我们比较了Set的has方法和数组indexOf:
数组/索引(0.281毫秒)|设置/具有(0.053ms)
2.增加一个新元素
我们分别比较Set和Array对象的add和push方法:
阵列/推送(1.612毫秒)|设置/添加(0.006ms)
3.删除要素
当删除元素时,我们必须记住Array和Set不是在相同的条件下启动的。Array没有本机方法,因此需要一个外部函数。
数组/从数组中删除(0.356毫秒)|设置/删除(0.019ms)
阅读完整文章here
g52tjvyc3#
只有属性查找,很少或零写入
如果属性查找是您主要关心的问题,这里有一些数字。
JSBench测试https://jsbench.me/3pkjlwzhbr/1
阵列
for
循环for
循环(反向)array.includes(target)
设置
set.has(target)
对象
obj.hasOwnProperty(target)
target in obj
速度降低〈-1.29%obj[target]
〈-最快Map
map.has(target)
速度降低〈-2.94%结果来自2021年1月,Chrome 87
您可以使用this spreadsheet制作一个漂亮的屏幕截图。*
myss37ts4#
对于您的问题的迭代部分,我最近运行了这个测试,发现Set的性能远远超过10,000项的Array(大约10倍的操作可能发生在同一时间段内)。并且取决于浏览器在同类测试中击败或丢失Object. hasOwnProperty。另一个有趣的点是对象没有官方保证的顺序,而JavaScript中的
Set
是作为有序集实现的,并保持插入顺序。Set和Object都有自己的"has"方法,执行起来似乎是O(1),但取决于浏览器的实现,单个操作可能需要更长或更快的时间。似乎大多数浏览器在Object中实现key比Set.has()更快。即使是Object. hasOwnProperty(包括对key的额外检查)也比Set.has()快5%,至少对我来说,在Chrome v86上是这样。
更新日期:2020年11月11日:https://jsbench.me/irkhdxnoqa/2
如果您想在不同的浏览器/环境下运行自己的测试。在此期间(当此测试运行时):Chrome V8显然只针对对象进行了优化:以下是Chrome v86在2020年11月的快照。
1.对于反循环:218074.48操作/秒‡ 0.59%操作/秒比非反向阵列高1.96倍。包括(9k次迭代)
Set.has
:154744804.61操作/秒‡ 1.88%709.6x比for循环反转的操作/秒多(由于目标位于右侧,因此仅需1k次迭代)hasOwnProperty
:161399953.02次操作/秒‡ 1.81%操作/秒比Set.has
高1.043倍key in myObject
:每秒883055194.54次操作‡ 2.08% ...每秒操作数比myObject.hasOwnProperty
高5倍。更新日期:2022年11月10日:今天我在Safari和Chrome上重新运行了同样的测试(在我的原始图像2年后),得到了一些有趣的结果:TLDR Set在两种浏览器上都比
key in Object
快,甚至快得多。Chrome还在某种程度上显著优化了Array.includes
,使其与Object/Set查找时间处于相同的速度范围内(而for循环要多花1000+倍的时间来完成)。对于Safari来说,Set明显比
key in Object
快,而Object. hasOwnProperty几乎不在同一速度范围内。所有数组变体(循环/包含)都比set/object查找慢得多。2022年11月10日快照:在Safari v16.1上测试的每秒操作数(更高=更快):
key in myObject
:942,192,599.63(速度降低39.25%,即使用Set时,每秒可以多执行约1.6倍的操作myObject.hasOwnProperty(key)
:21,363,224.51(慢98.62%)也就是说,您可以多执行大约72.6倍的Set. has操作,因为hasOwnProperty检查在1秒内完成。Array.includes(target)
111,076 ops/s比for循环手动检查目标要慢一点,您可以为每次include检查手动执行1.23次检查。在Chrome浏览器上www.example.com 2022年11月10日:v107.0.5304.87它们现在几乎相等了。(虽然预期的行为是set将优于Object,因为set与object的选项可能性更小,以及这是Safari中的行为。)值得注意的是,Array. includes显然在Chrome(v8)中至少针对此类测试进行了显著优化: It is no longer true that Set significantly underperforms Object
in
operation: they now nearly tie. (Though the expected behavior is that set would outperform Object in due to the smaller possible of options with a set vs an object and how this is the behavior in Safari.) Notably impressive Array.includes has apparently been significantly optimized in Chrome (v8) for at least this type of test:Object in
完成792894327.81次操作/秒‡ 2.51%最快Set.prototype.has
完成790523864.11次操作/秒‡ 2.22%最快Array.prototype.includes
完成679373215.29次操作/秒‡速度慢1.82% 14.32%Object.hasOwnProperty
已完成154217006.71次操作/秒‡速度降低1.31%和80.55%for loop
完成103015.26操作/秒+慢0.98% 99.99%7z5jn7bk5#
我的观察结果是,Set总是更好,但对于大型数组来说,它有两个缺陷:
a)从数组创建集合必须在
for
循环中完成,循环长度为预先缓存的长度。new Set(largeArray)
const SET = new Set(); const L = largeArray.length; for(var i = 0; i<L; i++) { SET.add(largeArray[i]) }
b)迭代也可以用同样的方法完成,因为它比
for of
循环更快...参见https://jsfiddle.net/0j2gkae7/5/
对于与具有40,000个元素的
difference()
、intersection()
、union()
和uniq()
(+其迭代同伴等)的实际比较8yparm6h6#
让我们考虑一下你想要维护一组唯一值的情况,你可以使用数组模拟几乎所有的Set操作:添加、删除、散列、清除和调整大小。
我们也可以实现迭代,但是注意它的行为和Set不太一样。Set是一个你可以在迭代过程中安全地修改而不跳过元素的集合,但是数组就不一样了。所以这里的比较并不是对所有用例都很公平。
虽然Set有更好的算法复杂度(O(1)比ArraySet的O(n)操作),但它可能在维护其内部树/散列时有更多的开销。在多大的规模下Set的开销是值得的?以下是我从基准测试NodeJS v18.12中收集的数据,用于一个普通用例(see benchmarking code):
正如预期的那样,我们看到了一个O(n)的加速集:
size
:等速add
:出现了一个有趣的模式,其中Set的性能根据其大小而波动。通常,数组在小于33个元素时会更快,而Set在大于52个元素时总是更快。delete
:数组对于小于10个元素更快has
:数组对于小于20个元素更快values
:设置总是更快iterator
(例如,for-of
环):设置总是更快在实践中,您可能不会创建自己的
ArraySet
类,而只是内联您感兴趣的特定操作。假设数组操作是内联的,性能会有什么变化?结果几乎相同,只是现在Array的迭代性能有所提高。内联
for-of
循环(不通过ArraySet
Package 类)现在总是比Set快。values
迭代器的速度大致相同。zvokhttg7#
这三个一万件物品的操作给了我: