我试图使用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
)的计算。 signal
和noise
对象具有包含数千个值的映射,因此计算中实际上使用了很多数据。
在并行计算中,日志产生很多以下行:
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
}
}