我在ConcurrentSkipListSet上使用了descendingIterator方法。我刚检查了文档并注意到以下评论:
'升序有序视图及其迭代器比降序视图快。'
不幸的是,它没有提供更多相关信息。有什么样的性能差异?它有意义吗?为什么会有性能差异?
答案 0 :(得分:13)
如果您查看Skip Lists的维基百科页面,您会发现它们实际上是一种复杂的链表,其中的链接指向列表条目的排序方向。 (该图清楚地说明了这一点......)
当您向前移动跳过列表时,您只需按照链接进行操作即可。每个next()
调用都是O(1)操作。
当您反向遍历跳过列表时,每个next()
调用必须在返回最后一个键之前找到键。这是一个O(logN)操作。
(但是,向后遍历跳过列表仍然比向后遍历单个链接列表快得多。对于每个next()
调用,这将是O(N)...)
如果你深入了解,你会发现ConcurrentSkipListSet
实际上是ConcurrentSkipListMap
的包装。在该类中,地图的跳过列表表示中的Node
个对象在升序键方向上单独链接链......它遵循(从前一个)上升迭代比下降迭代更快。
性能差异将是显着的,并且随着设置大小的增加,由于O(1)与O(logN)的差异,它将变得更加显着。
答案 1 :(得分:2)
除了斯蒂芬的回答,我写了一个简单的基准:
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
public class ConcurrentSkipListSetIteratorTest {
@Fork(1)
@Benchmark
public void ascItr(SetupParams params) {
Iterator<Integer> itr = params.cslset.iterator();
while (itr.hasNext()) itr.next();
}
@Fork(1)
@Benchmark
public void dscItr(SetupParams params) {
Iterator<Integer> itr = params.cslset.descendingIterator();
while (itr.hasNext()) itr.next();
}
@State(Scope.Benchmark)
public static class SetupParams {
private ConcurrentSkipListSet<Integer> cslset;
@Setup(Level.Invocation)
public void setUp() {
cslset = new SplittableRandom()
.ints(100_000, 0, 100_000)
.boxed()
.collect(Collectors.toCollection(ConcurrentSkipListSet::new));
}
}
}
主要方法:
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(ConcurrentSkipListSetIteratorTest.class.getSimpleName())
.jvmArgs("-ea", "-Xms512m", "-Xmx1024m")
.shouldFailOnError(true)
.build();
new Runner(opt).run();
}
此外,这是JDK 10
repository中的代码示例,它适用于升序和降序迭代器:
private void ascend() {
...
for (;;) {
// there is a link to the next node
next = next.next; // O(1) operation
...
}
}
private void descend() {
...
for (;;) {
// but, there is no link to the previous node
next = m.findNear(lastReturned.key, LT, cmp); // O(logN) operation
...
}
}
10_000
元素的最终结果:
Benchmark Mode Cnt Score Error Units
ascItr avgt 5 0,075 ± 0,029 ms/op
dscItr avgt 5 0,810 ± 0,116 ms/op
对于100_000
元素:
Benchmark Mode Cnt Score Error Units
ascItr avgt 5 2,764 ± 1,160 ms/op
dscItr avgt 5 11,110 ± 0,937 ms/op
可视化性能差异: