Что использовать в Kotlin / Java для эффективной обработки коллекций с плавающей запятой?

#android #performance #kotlin #memory

#Android #Производительность #kotlin #память

Вопрос:

Я работаю над приложением для редактирования звука, и мне нужно хранить сэмплы в виде чисел с плавающей запятой эффективным способом — с точки зрения памяти и производительности. Прямо сейчас я использую просто Kotlin’s List, но я слышал о потенциальных выгодах от использования типа FloatArray. Я провел синтетический тест, чтобы увидеть преимущества, и результаты немного странные. Любой совет о методах и коллекциях, которые я должен использовать для больших наборов данных такого рода, мог бы принести вам cookie, если бы у меня был один и я знал ваше местоположение.

Итак, у меня есть две альтернативные реализации канала (как in — channel в аудиофайле) для хранения моих данных.

Вот мой код для канала, основанного на FloatArray:

 class ArrayChannel {

    private var mData : FloatArray = FloatArray(0)
    private var mLastWrittenIndex = 0

    fun getSamples(startIndex : Int = 0, endIndex : Int = mLastWrittenIndex) : FloatArray 
        = mData.sliceArray(IntRange(0, endIndex - 1))

    fun insertSamples(samples : FloatArray, startIndex : Int = mLastWrittenIndex) {
        if (mData.size - mLastWrittenIndex < samples.size) {
            val newData = FloatArray(mData.size   samples.size )
            mData.copyInto(newData, 0, 0, startIndex)
            samples.copyInto(newData, startIndex)
            mData.copyInto(newData, startIndex   samples.size, startIndex)
            mData = newData
        } else {
            mData.copyInto(mData, startIndex   samples.size, startIndex, mLastWrittenIndex)
            samples.copyInto(mData, startIndex)
        }
        mLastWrittenIndex  = samples.size
    }

    fun getSamplesSize(): Int = mLastWrittenIndex
}
  

И вот мой код для канала, основанного на списке:

 class Channel {

    private var mData = mutableListOf<Float>()

    fun getSamples(startIndex : Int = 0, endIndex : Int = mData.size) : List<Float>
        = mData.slice(IntRange(0, endIndex - 1))

    fun insertSamples(samples : List<Float>, startIndex : Int = mData.size) {
        mData.addAll(startIndex, samples)
    }

    fun getSamplesSize() : Int = mData.size
}
  

Вот код измерения:

     val initialValuesArray = FloatArray(1000000) {Random.nextFloat()}
    val valuesToAddArray = FloatArray(1000000) {Random.nextFloat()}
    val initialValuesList = MutableList(1000000) {Random.nextFloat()}
    val valuesToAddList = MutableList(1000000) {Random.nextFloat()}

    var startTime = System.currentTimeMillis()
    val arrayChannel = ArrayChannel()
    arrayChannel.insertSamples(initialValuesArray)
    arrayChannel.insertSamples(valuesToAddArray, 0)
    println("Array time: ${System.currentTimeMillis() - startTime}")

    startTime = System.currentTimeMillis()
    val listChannel = Channel()
    listChannel.insertSamples(initialValuesList)
    listChannel.insertSamples(valuesToAddList, 0)
    println("List time: ${System.currentTimeMillis() - startTime}")
  

Теперь средние результаты прямого fun main() вызова в Android Studio следующие:

 Array time: 56
List time: 6
  

Изменение в распределении массива вместо mData.size samples.size to mData.size samples.size * 2 делает их разными очень странным образом:

 Array time: 17
List time: 48
  

Когда я запускаю тот же код внутри Activity вместо какой-либо основной функции Kotlin, результаты больше соответствуют моим ожиданиям и являются многообещающими:

 2020-08-17 21:15:33.325 D/ARRAY_TIME: 15
2020-08-17 21:15:33.481 D/LIST_TIME: 156
  

Почему код ведет себя таким образом и что, по вашему мнению, было бы хорошим способом обработки большого количества числовых данных в среде Android?

Комментарии:

1.Это недопустимый способ тестирования. Это игнорирует время прогрева и другие факторы. Поищите библиотеку сравнительного анализа. Не то чтобы вам нужен был тест, чтобы доказать, что a FloatArray не страдает от бокса, как List<Float> это делает a. В зависимости от того, что вы с этим делаете, вы можете обнаружить, что FloatBuffer сконструированный из direct ByteBuffer работает даже лучше, чем FloatArray .

2. Я изучу эти библиотеки для сравнительного анализа и обязательно попробую FloatBuffer . Кроме того, я смог решить некоторые проблемы с памятью с помощью разумного распределения массивов (кто бы мог подумать ;)) и пока я доволен результатами. Спасибо за помощь!