Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin 使用groupBy/groupingBy/aggregate并行求和到较小的存储桶中?_Kotlin_Coroutine_Kotlin Coroutines - Fatal编程技术网

Kotlin 使用groupBy/groupingBy/aggregate并行求和到较小的存储桶中?

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

我收集了一些“东西”,我想把它们汇总成更小的桶。(在我的特殊情况下,我对图像的luma通道进行了8倍的下采样。)

我希望它能在你的多核安卓设备上以尽可能快的速度运行,我认为这意味着每一个存储桶都要进行协同路由。(因为如果我不需要的话,没有任何理由玩Intaders)

朴素的线性解决方案:

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)
    }
    
    此外,通过这种方法,您可以轻松实现许多基于卷积运算的功能,如模糊和边缘检测