Kotlin 使用groupBy/groupingBy/aggregate并行求和到较小的存储桶中?
我收集了一些“东西”,我想把它们汇总成更小的桶。(在我的特殊情况下,我对图像的luma通道进行了8倍的下采样。) 我希望它能在你的多核安卓设备上以尽可能快的速度运行,我认为这意味着每一个存储桶都要进行协同路由。(因为如果我不需要的话,没有任何理由玩Intaders) 朴素的线性解决方案:Kotlin 使用groupBy/groupingBy/aggregate并行求和到较小的存储桶中?,kotlin,coroutine,kotlin-coroutines,Kotlin,Coroutine,Kotlin Coroutines,我收集了一些“东西”,我想把它们汇总成更小的桶。(在我的特殊情况下,我对图像的luma通道进行了8倍的下采样。) 我希望它能在你的多核安卓设备上以尽可能快的速度运行,我认为这意味着每一个存储桶都要进行协同路由。(因为如果我不需要的话,没有任何理由玩Intaders) 朴素的线性解决方案: val SCALE = 8 image.planes[0].buffer.toByteArray().forEachIndexed { index, byte -> val x1 = index
val SCALE = 8
image.planes[0].buffer.toByteArray().forEachIndexed { index, byte ->
val x1 = index % image.width
val y1 = index / image.width
val x2 = x1 / SCALE
val y2 = y1 / SCALE
val quadIdx = y2 * (image.width / SCALE) + x2
summedQuadLum[quadIdx] += (byte.toInt() and 0xFF)
}
这不是很好-需要预先声明summedQuadLum
集合,并且没有任何并行工作的机会
我想使用groupBy
,或者groupingBy
?或者aggregate
?),但这些似乎都使用值来确定新键,我需要使用键来确定新键。我认为最小的开销是和index
,这可以像
val thumbSums = bufferArray.withIndex().groupingBy { (idx, _) ->
val x1 = idx % previewImageDimension.width
val y1 = idx / previewImageDimension.width
val x2 = x1 / SCALE
val y2 = y1 / SCALE
y2 * (previewImageDimension.width / SCALE) + x2
}.aggregate { _, acc: Int?, (_, lum), _ ->
(acc ?: 0) + (lum.toInt() and 0xFF)
}.values.toIntArray()
更好的是,真的很接近-如果我能找出如何对一个协同过程中的每个存储桶求和,我认为这将与预期的一样好。因此在
groupingBy
之后,我们有了一个分组
对象,我们可以使用它来聚合值。重要的是要注意分组本身还没有完成,我们基本上已经描述了如何对值和原始数组的迭代器进行分组。从这里我们有几个选择:
suspend fun sample() {
val asyncFactor = 8
val src = Image(bufferArray, width)
val dst = Image(src.width / SCALE, src.height / SCALE)
val chunkSize = dst.sizeBytes / asyncFactor
val jobs = Array(asyncFactor) { idx ->
async(Dispatchers.Default) {
val chunkStartIdx = chunkSize * idx
val chunkEndIdxExclusive = min(chunkStartIdx + chunkSize, dst.sizeBytes)
calculateSampledImageForIndexes(src, dst, chunkStartIdx, chunkEndIdxExclusive, SCALE)
}
}
awaitAll(*jobs)
}
private fun calculateSampledImageForIndexes(src: Image, dst: Image, startIdx: Int, exclusiveEndIdx: Int, scaleFactor: Int) {
for (i in startIdx until exclusiveEndIdx) {
val destX = i % dst.width
val destY = i / dst.width
val srcX = destX * scaleFactor
val srcY = destY * scaleFactor
var sum = 0
for (xi in 0 until scaleFactor) {
for (yi in 0 until scaleFactor) {
sum += src[srcX + xi, srcY + yi]
}
}
dst[destX, destY] = sum / (scaleFactor * scaleFactor)
}
}
其中,Image
是图像数据缓冲区的方便包装:
class Image(val buffer: ByteArray, val width: Int) {
val height = buffer.size / width
val sizeBytes get() = buffer.size
constructor(w: Int, h: Int) : this(ByteArray(w * h), w)
operator fun get(x: Int, y: Int): Byte = buffer[clampX(x) * width + clampY(y)]
operator fun set(x: Int, y: Int, value: Int) {
buffer[x * width + y] = (value and 0xFF).toByte()
}
private fun clampX(x: Int) = max(min(x, width), 0)
private fun clampY(y: Int) = max(min(y, height), 0)
}
此外,通过这种方法,您可以轻松实现许多基于卷积运算的功能,如模糊和边缘检测