为什么用RxJava进行并行计算要比顺序计算慢?

时间:2019-12-28 16:04:47

标签: parallel-processing rx-java rx-java2

我试图使用RxJava多线程加快计算速度,但是并行计算比顺序计算花费更多的时间。 我在数组中有数百个值。对于每个值,我都需要进行长时间的计算。 并行计算:

            array.toFlowable()
                .parallel()
                .runOn(Schedulers.computation())
                .map { value ->
                    longCalculations(value)
                }
                .sequential()
                .observeOn(Schedulers.io())
                .subscribe { result ->
                    // use results
                }

顺序计算:

            array.toFlowable()
                .map { value ->
                    longCalculations(value)
                }
                .subscribeOn(Schedulers.computation())
                .observeOn(Schedulers.io())
                .subscribe { result ->
                    // use results
                }

我使用System.nanoTime()测量时间。并行过程大约需要100秒,而顺序过程需要60秒。那么,为什么在8个线程(以我为例)中的计算要比在一个线程中慢?


更新

我正在测试96个(pointsCount)双重类型项目(snr)的计算。 signalnoise对象具有包含数千个值的映射,因此计算中实际上使用了很多数据。

在并行计算中,日志产生很多以下行:

WaitForGcToComplete blocked Alloc on HeapTrim for 572.951ms
Starting a blocking GC Alloc
Waiting for a blocking GC Alloc

更多代码:

class CalculateCharacteristicsProcess(
    private val signal: Signal,
    private val demodulatorConfig: DemodulatorConfig,
    private val channels: List<Channel>,
    private val threshold: Float
) {
    @Inject
    lateinit var storage: Repository
    private var disposable: Disposable? = null

    init {
        App.component.inject(this)
    }

    fun execute(
        fromSnr: Double,
        toSnr: Double,
        pointsCount: Int,
        progress: (Int) -> Unit = {}
    ) {
        val step = (toSnr - fromSnr) / pointsCount
        val snrs = DoubleArray(pointsCount) { fromSnr + it * step }
        var pointsCalculated = 0

        disposable = snrs.toFlowable()
            .parallel()
            .runOn(Schedulers.computation())
            .map { snr ->
                val ber = calculateBer(snr, signal, demodulatorConfig, channels, threshold)
                val capacity = calculateCapacity(snr, channels.first().bitTime)
                Triple(snr, ber, capacity)
            }
            .sequential()
            .observeOn(Schedulers.io())
            .doOnSubscribe {
                progress(0)
                println("${System.nanoTime()}") // Start measuring
            }
            .subscribe({
                pointsCalculated++
                val p = (pointsCalculated / snrs.size.toDouble() * 100).toInt()
                progress(p)
                storage.setBerByNoise(it.first to it.second)
                storage.setCapacityByNoise(it.first to it.third)
            },
                { progress(100) },
                {
                    println("${System.nanoTime()}") // End measuring
                    progress(100)
                })
    }

    fun cancel() {
        disposable?.dispose()
    }

    private fun createNoise(snr: Double): Noise {
        return WhiteNoise(snr, QpskContract.DEFAULT_SIGNAL_POWER)
    }

    private fun demodulate(ether: Signal, config: DemodulatorConfig): DoubleArray {
        return QpskDemodulator(config).demodulateFrame(ether).dataValues
    }

    private fun decode(groupData: DoubleArray, code: BooleanArray, threshold: Float): DoubleArray {
        return CdmaDecimalCoder(threshold).decode(code.toBipolar(), groupData)
    }

    private fun calculateBer(
        snr: Double,
        signal: Signal,
        demodulatorConfig: DemodulatorConfig,
        channels: List<Channel>,
        threshold: Float
    ): Double {
        val noise = createNoise(snr)
        val ether = signal + noise
        val groupData = demodulate(ether, demodulatorConfig)
        val bitsWithErrors = channels.fold(0 to 0) { acc, channel ->
            val data = decode(groupData, channel.code, threshold)
            val bits = data.size
            val errors = data.count { it == 0.0 }
            (acc.first + bits) to (acc.second + errors)
        }
        return bitsWithErrors.second / bitsWithErrors.first.toDouble() * 100.0
    }

    private fun calculateCapacity(
        snr: Double,
        bitTime: Double
    ): Double {
        val bandwidth = 1 / bitTime
        val linearSnr = 10.0.pow(snr / 10)
        return bandwidth * log2(1 + linearSnr) * 1.0e-3
    }
}

0 个答案:

没有答案