Почему последовательность Kotlin так плохо работает с небольшим количеством элементов? - PullRequest
1 голос
/ 09 марта 2020

Я пытался сравнить производительность последовательности Kotlin с коллекцией Kotlin: Kotlin Playground , и я ожидал, что последовательность будет превосходить коллекцию каждый раз, но это не для наименьшего числа элементов 10, вот мои результаты:

==============================
Size 10
Lista: 62835 ns
Sekvenca: 7050134 ns
==============================

==============================
Size 100
Lista: 96139 ns
Sekvenca: 10376 ns
==============================

==============================
Size 1000
Lista: 702532 ns
Sekvenca: 9689 ns
==============================

==============================
Size 10000
Lista: 2598859 ns
Sekvenca: 26116 ns
==============================

==============================
Size 100000
Lista: 11004099 ns
Sekvenca: 45011 ns
==============================

==============================
Size 1000000
Lista: 623017128 ns
Sekvenca: 55156 ns
==============================

Я не понимаю, почему это может кто-то уточнить? Потому что в следующий раз, когда я запустил его, я добавил еще один размер 5, но на этот раз размер 10 показал результаты в пользу последовательности, возможно, это какая-то ошибка?

Ответы [ 2 ]

3 голосов
/ 09 марта 2020

Вот как ваши результаты выглядят на графике журнала:

urlich201 Benchmark: List vs. Sequence

Вместо ожидаемых двух прямых, мы получаем в значительной степени бессмысленные кривые. Например, кажется, что обработка последовательности из 10 элементов в 200 раз дороже, чем обработка 100 000.

Я улучшил ваш эталонный тест, применив некоторые основы микробенчмаркинга в JVM, такие как учет прогрева и не игнорирование Результат вычисления:

val size = 10
val warmupIterations = 100_000
val measureIterations = 100_000
val collection = List(size) { Weather((-10..10).shuffled().first()) }
val sequence = collection.asSequence()

var controlSum = 0L

fun main() {
    val (a, b) = benchmark(warmupIterations)
    println("Warmed up: $a $b")

    val (listInNanos, sequenceInNanos) = benchmark(measureIterations)

    println("Control sum: $controlSum")
    println("size $size")
    println("list %.0f sequence %.0f".format(listInNanos, sequenceInNanos))
}

private fun benchmark(iterations: Int): Pair<Double, Double> {
    val listInNanos = (1..iterations).map {
        measureNanoTime {
            controlSum += listApi(collection)
        }
    }.average()

    val sequenceInNanos = (1..iterations).map {
        measureNanoTime {
            controlSum += sequencApi(sequence)
        }
    }.average()
    return Pair(listInNanos, sequenceInNanos)
}

private fun sequencApi(sequence: Sequence<Weather>): Int {
    return (sequence
            .map { it.temperature }
            .filter { it % 2 == 0 }
            .firstOrNull { it < 0 }) ?: -1
}

private fun listApi(collection: List<Weather>): Int {
    return (collection
            .map { it.temperature }
            .filter { it % 2 == 0 }
            .firstOrNull { it < 0 }) ?: -1
}

Это мои результаты:

mtopolnik Benchmark: List vs. Sequence

По сравнению с вашими результатами стоимость обработки 10 предметов теперь в 100 раз меньше с API-интерфейсом Collection и в 1000000 раз меньше с API-интерфейсом Sequence.

Основной вывод: для вашего случая использования Sequence обработка дешевле, чем Collection обработка по всем направлениям.

0 голосов
/ 09 марта 2020

Это не имеет никакого отношения к размеру списка, у последовательности просто немного больше издержек.

Когда вы выполняете первый l oop jvm должен загрузить классы, необходимые для последовательности, в память. Вы можете изменить свою игровую площадку, добавив сначала список размера 1, и это значительно сократит время расчетов размера 10.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...