【问题标题】:Best way to handle continuous flow of small byte arrays that need to be flattened?处理需要展平的小字节数组连续流的最佳方法?
【发布时间】: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 的分配)、LinkedListArray 的 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


【解决方案1】:

我不相信您的问题与缓冲区分配有关,但我们使用 ByteArrayOutputStream 来聚合来自特征通知的 MTU 大小的段。

ByteArrayOutputStream inputBuffer = new ByteArrayOutputStream();

public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic chr) {
  inputBuffer.write(chr.getValue());
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多