具有单个生产者和单个消费者的环形缓冲区的最佳大小
Optimal size for a ring buffer with single producer and single consumer
我有一个单一的生产者,单一的消费者问题,(我相信)可以使用 circular/ring 缓冲区来解决。
我有一个微控制器 运行 一个 RTOS,带有一个 ISR(中断服务程序)处理 UART(串行端口)中断。当 UART 产生中断时,ISR 将接收到的字符发送到循环缓冲区中。在另一个 RTOS 任务(称为 packet_handler)中,我正在从这个循环缓冲区和 运行 状态机读取数据包。一个有效的数据包有 64 个字节,包括所有的帧字节。
串口工作在115200,每10ms一个数据包到达。 packet_handler 每 10 毫秒运行一次。但是,由于另一个更高优先级的任务正在执行,此处理程序有时可能会被调度程序延迟。
如果我使用任意大的循环缓冲区,则不会出现丢包。但是在这种情况下如何确定最佳循环缓冲区大小呢?至少理论上是这样。这个缓冲区大小的决定在实践中是如何做出的?
目前,我正在通过一些检测函数检测缓冲区溢出,然后增加缓冲区大小以减少数据包丢失。
我会这样计算:
64字节收到才知道
64 字节仍在环形缓冲区中
+100% 保存
===================
256 字节缓冲区
但这只是一个猜测。你必须用你的缓冲区做一些最坏的情况测试,然后花费 +100% 来保存。
你永远不会安全,因为你正在处理一个随机过程(根据你的解释)。
回答您的问题:您将需要一个无限缓冲区,以防万一消费者任务处于就绪状态无限秒。所以,你必须改变你最初的方法:
- 提高consumer的优先级,以保证10ms的执行(最小buffer的做法,但不一定可行)
- 尝试更好地表征您的模型,以便预测不会执行消费者任务的最大时间间隔(让您的系统尽可能可预测)。
- 丢失具有随机缓冲区大小的包(这可能不安全)
虽然以上所有答案都是正确的并且可以说明问题,但 this page 总结了在选择环形缓冲区大小时要考虑的所有因素。
可以使用一些排队模型从理论上分析手头的问题,找出合适的环形缓冲区大小。
更实用的方法是从一个大缓冲区开始,然后找出实际测试用例中使用的最大缓冲区大小(这个过程称为加水印),并在最终代码中使用这个数字。
这只是确定最大可能延迟的问题 - 所有更高优先级任务的执行时间总和 运行 - 并将其除以数据包到达间隔。
这可能并不简单,但可以通过仅使最确定性的任务具有更高的优先级并根据速率单调原则将不太确定性和更长的 运行ning 任务移动到较低的优先级来简化任务经常 运行 很短的时间,但偶尔 运行 更长的时间是被分成两个任务(和更多队列)以将较长的执行卸载到较低优先级的候选人。
我有一个单一的生产者,单一的消费者问题,(我相信)可以使用 circular/ring 缓冲区来解决。
我有一个微控制器 运行 一个 RTOS,带有一个 ISR(中断服务程序)处理 UART(串行端口)中断。当 UART 产生中断时,ISR 将接收到的字符发送到循环缓冲区中。在另一个 RTOS 任务(称为 packet_handler)中,我正在从这个循环缓冲区和 运行 状态机读取数据包。一个有效的数据包有 64 个字节,包括所有的帧字节。
串口工作在115200,每10ms一个数据包到达。 packet_handler 每 10 毫秒运行一次。但是,由于另一个更高优先级的任务正在执行,此处理程序有时可能会被调度程序延迟。
如果我使用任意大的循环缓冲区,则不会出现丢包。但是在这种情况下如何确定最佳循环缓冲区大小呢?至少理论上是这样。这个缓冲区大小的决定在实践中是如何做出的?
目前,我正在通过一些检测函数检测缓冲区溢出,然后增加缓冲区大小以减少数据包丢失。
我会这样计算:
64字节收到才知道
64 字节仍在环形缓冲区中
+100% 保存
===================
256 字节缓冲区
但这只是一个猜测。你必须用你的缓冲区做一些最坏的情况测试,然后花费 +100% 来保存。
你永远不会安全,因为你正在处理一个随机过程(根据你的解释)。 回答您的问题:您将需要一个无限缓冲区,以防万一消费者任务处于就绪状态无限秒。所以,你必须改变你最初的方法:
- 提高consumer的优先级,以保证10ms的执行(最小buffer的做法,但不一定可行)
- 尝试更好地表征您的模型,以便预测不会执行消费者任务的最大时间间隔(让您的系统尽可能可预测)。
- 丢失具有随机缓冲区大小的包(这可能不安全)
虽然以上所有答案都是正确的并且可以说明问题,但 this page 总结了在选择环形缓冲区大小时要考虑的所有因素。
可以使用一些排队模型从理论上分析手头的问题,找出合适的环形缓冲区大小。
更实用的方法是从一个大缓冲区开始,然后找出实际测试用例中使用的最大缓冲区大小(这个过程称为加水印),并在最终代码中使用这个数字。
这只是确定最大可能延迟的问题 - 所有更高优先级任务的执行时间总和 运行 - 并将其除以数据包到达间隔。
这可能并不简单,但可以通过仅使最确定性的任务具有更高的优先级并根据速率单调原则将不太确定性和更长的 运行ning 任务移动到较低的优先级来简化任务经常 运行 很短的时间,但偶尔 运行 更长的时间是被分成两个任务(和更多队列)以将较长的执行卸载到较低优先级的候选人。