我有一台产生数据流的工具;我的代码通过回调onDataAcquisitionEvent(const InstrumentOutput &data)
访问此数据。数据处理算法可能比数据到达率慢得多,因此我不希望处理每一个数据(我不必),但希望尽可能多地处理。感谢仪器作为环境传感器,具有我无法控制的数据采集速率。 InstrumentOutput
例如可以是在不同位置包含三个同时压力测量值的类。
我还需要保留一些简短的数据历史记录。例如,假设我可以合理地希望每200ms左右处理一个数据样本。大多数时候我会很乐意处理一个最后一个样本,但偶尔我需要查看在最新样本之前到达的几秒钟的数据,这取决于最后一个样本中是否存在异常读数。
另一个要求是尽快退出onDataAcquisitionEvent()
回调,以避免传感器中的数据丢失。
数据采集库(第三方)在单独的线程上收集仪器数据。
我想到了以下设计;具有单个生产者/单个使用者队列并将数据标记推送到onDataAcquisitionEvent()回调中的同步队列中。
在接收端,有一个循环从队列中弹出数据。由于数据到达率高,循环几乎不会睡眠。在每次迭代时,会发生以下情况:
问题:
编辑:我想到的一个问题是当循环缓冲区的大小不足以容纳所需的历史记录时;目前我只是重新分配循环缓冲区,使其大小加倍。我希望我只需要这样做一两次。
答案 0 :(得分:3)
我在数据采集方面有一些经验,我可以告诉你很多开发人员遇到过早的功能蠕变问题。因为简单地将仪器中的数据捕获到日志中听起来很容易,所以人们倾向于在验证记录实际上是否健壮之前向系统添加不必要的组件。这是一个很大的错误。
另一个要求是尽快退出
onDataAcquisitionEvent()
回调,以避免传感器中的数据丢失。
这是唯一的要求,直到该部分产品在所有现场条件下工作110%。
大多数时候我会很乐意处理最后一个样本,但偶尔我需要查看在最新样本之前到达的几秒钟的数据,具体取决于是否存在异常读数。最后一个样本。
“大部分时间”并不重要。最坏情况的代码,因为onDataAcquisitionEvent()
无法花时间考虑意外情况。
听起来你正陷入设计它以利用可能获得的最佳数据的陷阱,并且如果它不可用或者向显示器提供最佳数据最终会太昂贵
在源处取消数据。指定异常案例处理需要多少样本,并尝试以恒定的采样率提供许多样本,加上可能为20%的边际。
肯定没有永不睡觉的循环。循环缓冲区很好,但只需要用你需要的最小值填充它,并且只在必要时进行分析。
系统的质量取决于其稳定性和确定性,而不是试图加倍努力并尽可能多地提供。
答案 1 :(得分:0)
您的生产者/消费者设计是正确的设计。在实时系统中,我们通常还会为消费线程提供不同的运行时优先级,但不确定这适用于您的情况。
使用基本上是双向链表的数据结构,这样如果它增长,你不需要重新分配所有内容,并且你也可以O(1)访问你需要的样本。
如果你的内存不够大,无法容纳几秒钟的数据(它应该 - 每200毫秒一个样本?每秒5个样本。)那么你需要看看你是否可以从辅助内存中读取数据,但这是吞吐量,在您的情况下与您的设计和“尽快退出回调”的要求无关。
考虑一个不需要锁定的队列的实现(记住:单个读者和单个编写者!),这样你的回调就不会停滞。
如果您的回调非常快,请考虑禁用中断/赋予其高优先级。如果它永远不会阻止并且设置了正确的优先级,则可能没有必要。
答案 2 :(得分:0)
问题,(1)是这种设计的声音,有什么缺陷,以及(2)什么是更好的设计。感谢。
是的,这是合理的。但出于性能原因,您应该设计代码,以便在每个处理阶段处理输入样本数组,而不是每个处理阶段只处理一个样本。这为当前最先进的CPU提供了更优化的代码。
这样一个数组(=一大块数据)的长度要么是固定的(更简单的代码),要么是可变的(灵活的,但有些处理可能会变得更复杂)。
作为第二个设计选择,你可能应该忽略这个架构层面的历史,并放弃这个特征......
大多数时候我会很乐意处理最后一个样本,但偶尔我需要查看几秒钟的数据[...]
也许,跟踪历史记录应该只在代码的特殊部分实现,偶尔需要访问它。也许,这不应该是“整体架构”的一部分。如果是这样,它简化了处理。