使用Scala的scala.collection.Set[T]
。给定一个只有几个元素的小集s
和另一个有很多元素的大集b
,两者之间是否存在性能差异?
s & b // s intersect b
和
b & s // b intersect s.
如果是,哪个最快?
答案 0 :(得分:2)
答案是:很复杂。
不可变集的默认实现为scala.collection.immutable.Set。它的大小为1到4的special cases。一旦您拥有4个以上的元素,它将使用scala.collection.immutable.HashSet。
因此,假设您有一个大集合b
和一个小集合s
,其中s
包含<4个元素。
然后它将产生大差异:
b & s
将针对b
中的成员资格过滤s
的所有元素,因此进行b.count * s.count相等比较。由于b很大,所以会很慢。
s & b
将针对s
中的成员资格过滤b
的几个元素,这是s.length乘以哈希值和相等性比较(如果哈希匹配)(记住b是a HashSet)。因为很小,所以应该很快。
s
大于4个元素时,也将成为HashSet。 HashSet的交集以对称且非常有效的方式编写。它将结合s
和b
的树结构,并在哈希匹配时执行相等比较。它将使用最大的结构共享。例如。如果b
包含s
的所有元素,则结果将与s 相同的实例,因此将不分配对象。
如果您只是在不太关心高性能的情况下编写通用代码,则可以使用诸如scala.collection.Set
之类的默认实现。但是,如果您关心性能特征,则最好使用 concrete 实现。例如。如果将s
和b
声明为scala.collection.immutable.HashSet
,只要您具有良好的哈希函数,您将获得与顺序无关的一致高性能。
答案 1 :(得分:1)
在GenSetLike
中使用intersect
的通用实现被HashSet
所覆盖,但该实现对我来说似乎很复杂(请参阅scala.collection.immutable.HashSet.HashTrieSet#intersect0)。根据我的粗略基准测试,a & b
和b & a
的性能相似,并且与a filter b
的性能相似,这比b filter a
快一个数量级。我的测试代码是:
object Sets extends App {
def time[R](block: => R): R = {
val t0 = System.nanoTime()
val result = block // call-by-name
val t1 = System.nanoTime()
println("Elapsed time: " + (t1 - t0)/1e6 + "ms")
result
}
val a = (0 until 10000 by 1).toSet //smaller data
val b = (0 until 1000000 by 2).toSet
time {a & b}
time {b & a}
time {a & b}
time {b & a}
time {a & b}
time {b & a}
println("Filter")
time {a filter b}
time {b filter a}
time {a filter b}
time {b filter a}
time {a filter b}
time {b filter a}
}
结果是:
Elapsed time: 6.990442ms Elapsed time: 4.25017ms Elapsed time: 4.1089ms Elapsed time: 4.480789ms Elapsed time: 3.71588ms Elapsed time: 3.160159ms Filter Elapsed time: 7.781613ms Elapsed time: 68.33023ms Elapsed time: 5.858472ms Elapsed time: 42.491131ms Elapsed time: 2.982364ms Elapsed time: 52.762474ms
答案 2 :(得分:0)
让我们根据提到的条件创建两个集合
val a = (0 until 10000 by 1).toSet //smaller data
val b = (0 until 1000000 by 2).toSet //Relatively larger data
我们可以定义一个时间函数来检查执行时间,如下所示
def time[R](block: => R): R = {
val t0 = System.nanoTime()
val result = block // call-by-name
val t1 = System.nanoTime()
println("Elapsed time: " + (t1 - t0) + "ns")
result
}
现在我们可以检查相交条件
scala> time {a & b}
Elapsed time: 5895220ns
res2: scala.collection.immutable.Set[Int] = Set(892, 5810, 8062, ..)
scala> time {b & a}
Elapsed time: 6038271ns
res3: scala.collection.immutable.Set[Int] = Set(892, 5810, 8062, ...)
因此,我们可以得出这样的结论:较小的数据集和较大的数据集之间的交集具有性能差异,最好将较小的数据集放在左侧以便更快地执行Scala集