【发布时间】:2019-12-12 13:39:03
【问题描述】:
我正在通过NOTIFY 从 BLE 服务器接收大量数据。在该应用的 iOS 版本中,这种传输非常简单,并且没有性能下降,但是在 Android 上,我似乎找不到性能不差的实现。
最初的实现使用了一个简单的 ByteArray 覆盖:
// Warning! Semi-pseudocode
var rawData = ByteArray(0)
characteristic.onReceive { data ->
rawData += data
}
在传输大约 3MB 之前,这一切正常。在那之后,任务速度变慢了(例如,第一个 10% - ~1MB - 将在一分钟内传输,第二个 10% 将在一分钟左右,第三个 10% 几乎 2 分钟,然后所有后续批次的时间大约增加前一批的金额,想想斐波那契数列)。我理解这是因为每次调用 onReceive 时,我都会创建一个新的 ByteArray 并丢弃之前的对象,最后导致 GC 收集 2-3MB 的对象。
这显然不是最优的,尤其是当完整传输约为 16MB 时。
我尝试了预分配的ByteBuffer(本地和基于 Java 的分配)、LinkedList 和 Array 的 ByteArrays,等等。
onReceive 部分使用 Kotlin Coroutines(ReceiveChannel<ByteArray> 和 Flow<ByteArray>)解决,如果我不保存数据,它甚至比 iOS 更快(主要是由于 MTU 差异 - 162 与 250收到的字节数)。
将这些收到的批次整理成单个 ByteArray 的最佳方法是什么?
【问题讨论】:
-
预分配缓冲区的坏处是什么?这通常通过以指数级调整大小(大小 *= 1.5)来实现。
-
@MarkoTopolnik 除了速度急剧下降之外,真的没什么。使用预分配的 ByteBuffer 时,传输速度下降到我在每次通知时重新创建 ByteArray 时所经历的速度。所以基本上,比如说,使用 ByteArray,第一个 MB 需要 1 分钟,第五个 MB 需要大约 6 分钟,使用 ByteBuffer,第一个 MB 也需要大约 6 分钟。
-
但这没有任何意义,如果你真的在开始时只分配一次。如果这表现不佳,原因不是您预先分配了缓冲区,而是您尚未提出的其他内容。
-
另一点:几乎一分钟内 1 MB 转换为微不足道的 130 kbit/s,解决方案必须真的弄错了,甚至无法跟上.以指数方式调整缓冲区大小只会为完全预分配的缓冲区增加 O(1) 每字节开销。
标签: android performance bluetooth-lowenergy kotlin-coroutines