#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
сконструированный из directByteBuffer
работает даже лучше, чемFloatArray
.2. Я изучу эти библиотеки для сравнительного анализа и обязательно попробую
FloatBuffer
. Кроме того, я смог решить некоторые проблемы с памятью с помощью разумного распределения массивов (кто бы мог подумать ;)) и пока я доволен результатами. Спасибо за помощь!