Why are ConcurrentSkipListSet ascending Iterators 'faster' than descending ones?

情到浓时终转凉″ 提交于 2019-12-21 07:25:12

问题


I’m using the descendingIterator method on ConcurrentSkipListSet. I’ve just checked the documentation and noticed the following comment:

‘Ascending ordered views and their iterators are faster than descending ones.’

See https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentSkipListSet.html#descendingIterator--

Unfortunately it doesn’t provide any more information on this. What kind of performance difference is there? is it significant? and why is there a performance difference?


回答1:


If you look at the Wikipedia page for Skip Lists you will see that they are effectively a complicated form of linked list with the links going in the direction of an ordering of the list entries. (The diagram illustrates this clearly ...)

When you traverse the skip list in the forward direction, you are simply following the links. Each next() call is an O(1) operation.

When you traverse the skip list in the reverse direction, each next() call has to find the key before the last key returned. This is an O(logN) operation.

(However, traversing a skip list backwards is still substantially faster than traversing a singly linked list backwards. That would be O(N) for each next() call ...)

If you look under the hood, you will see that a ConcurrentSkipListSet is actually a wrapper for a ConcurrentSkipListMap. In that class, the Node objects in the skip list representation of the map form singly linked chains ... in the ascending key direction. It follows (from the previous) that ascending iteration is faster than descending iteration.

The performance difference will be significant, and it will get more significant as the set size increases because of the O(1) versus O(logN) difference.




回答2:


In addition to Stephen's answer, I wrote a simple Benchmark:

@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));
        }
    }
}

Main method:

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();
}

Also, here is a code example from the JDK 10 repository that is used in ascending and descending iterators appropriately:

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
        ...
    }
}

Final results for 10_000 elements:

Benchmark  Mode  Cnt  Score   Error  Units
ascItr     avgt    5  0,075 ± 0,029  ms/op
dscItr     avgt    5  0,810 ± 0,116  ms/op

And for 100_000 elements:

Benchmark  Mode  Cnt   Score   Error  Units
ascItr     avgt    5   2,764 ± 1,160  ms/op
dscItr     avgt    5  11,110 ± 0,937  ms/op

Visualizing performance difference:



来源:https://stackoverflow.com/questions/50738555/why-are-concurrentskiplistset-ascending-iterators-faster-than-descending-ones

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!