Algorithm 计划以不同的速率向消费者发送消息

Algorithm 计划以不同的速率向消费者发送消息,algorithm,optimization,scheduling,Algorithm,Optimization,Scheduling,我正在寻找消息调度的最佳算法。我的意思是,当我们有许多不同速率的消费者时,消息调度是在公交车上发送消息的一种方式 例如: 假设我们有数据D1到Dn . D1每5ms发送一次C1,每19ms发送一次C2,每30ms发送一次C3,每Rn ms发送一次Cn . Dn每10毫秒发送一次到C1,C2每31毫秒发送一次,Cn每50毫秒发送一次 以最佳性能(CPU、内存、IO)调度此操作的最佳算法是什么 关于我可以想出很多选择,每个都有各自的成本和收益。这实际上可以归结为你的需求到底是什么——是什么真正为你定

我正在寻找消息调度的最佳算法。我的意思是,当我们有许多不同速率的消费者时,消息调度是在公交车上发送消息的一种方式

例如: 假设我们有数据D1到Dn . D1每5ms发送一次C1,每19ms发送一次C2,每30ms发送一次C3,每Rn ms发送一次Cn . Dn每10毫秒发送一次到C1,C2每31毫秒发送一次,Cn每50毫秒发送一次

以最佳性能(CPU、内存、IO)调度此操作的最佳算法是什么


关于

我可以想出很多选择,每个都有各自的成本和收益。这实际上可以归结为你的需求到底是什么——是什么真正为你定义了“最好的”。我在下面对一些可能性进行了伪编码,希望能帮助您开始

选项1:每次执行以下时间单位(在您的示例中为毫秒)

这样做的好处是不需要一致存储的内存——您只需在每个时间单位检查您是否应该发送消息。这还可以处理在
时间==0时未发送的消息--只需存储消息最初发送的时间,并将其与速率进行模化,如果时间%datum.customer.rate==data.customer.firstMsgTimeMod
,则将条件替换为

这种方法的一个缺点是,它完全依赖于总是以1毫秒的速率调用。如果CPU上的另一个进程导致延迟,并且它错过了一个周期,那么您可能会完全错过发送消息(而不是稍微晚一点发送)

选项2:维护元组列表,其中每个条目表示需要在该毫秒完成的任务。将您的列表长度至少与最长速率除以时间单位的长度相同(如果您的最长速率为50毫秒,并且以毫秒为单位,则您的列表长度必须至少为50毫秒)。启动程序时,将消息第一次发送到队列中。然后,每次发送消息时,在下次发送该列表时进行更新

func buildList(&list)
    for each datum
        for each customer
            if list.size < datum.customer.rate
                list.resize(datum.customer.rate+1)
            list[customer.rate].push_back(tuple(datum.name, customer.name))

func callEachMs(&list)
    for each (datum.name, customer.name) in list[0]
        sendMsg()
        list[customer.rate].push_back((datum.name, customer.name))
    list.pop_front()
    list.push_back(empty list)
func构建列表(&list)
对于每个基准面
针对每位客户
如果列表尺寸
这样做的优点是避免了选项1所需的许多不必要的模量计算。但是,这会带来内存使用量增加的成本。如果您的各种消息的速率存在很大差异,那么这种实现也不会有效率(尽管您可以修改它以更有效地处理速率较长的算法)。它仍然必须每毫秒被调用一次

最后,您必须非常仔细地考虑您使用的数据结构,因为这将对其效率产生巨大的影响。因为在每次迭代中都从前面弹出,从后面推,并且列表的大小是固定的,所以您可能希望实现一个列表,以避免不必要的值移动。对于元组列表,由于它们只经过迭代(不需要随机访问),并且有频繁的添加,因此单链接列表可能是最好的解决方案


显然,有很多方法可以做到这一点,但希望这些想法能让你开始。另外,请记住,运行此操作的系统的性质可能会对哪种方法工作得更好产生很大影响,或者您是否希望完全执行其他操作。例如,这两种方法都要求能够以一定的速率可靠地调用它们。我还没有描述并行化实现,如果您的应用程序支持并行化实现,这可能是最好的选择。

与前面描述的Hemium_1s2一样,还有第二种方法是基于我称之为时间表的,这就是我现在使用的方法,但这种解决方案有其局限性

假设我们有一个数据要发送,两个消费者C1和C2:

如您所见,我们必须提取调度表,并且必须确定重复传输周期和空闲最小周期的值。事实上,在最小的和平时间(例如1ms或1ns或1mn或1h,视情况而定)上循环是没有用的,但它并不总是最佳周期,我们可以如下优化此循环

例如一个(C1在6,C2在9),我们注意到有一个循环从0到18重复。两个连续发送事件的最小差值等于3。 因此:

时间表如下:

发送循环如下所示:

while(1) {
  sleep(IDLE_MINIMUM_PERIOD); // free CPU for idle min period
  i++; // initialized at 0
  send(ScheduleTable[i]);
  if (i == sizeof(ScheduleTable)) i=0;
}

这种方法的问题是,如果LCM增长,这个数组就会增长,如果我们有坏的组合,比如速率=素数,等等,这种情况就是如此。

我认为第一个命题非常有限,因为它是指数复杂度。因此,当数据/使用者数量增加时,性能会显著降低。从我的角度来看,我已经对第二个问题进行了编码(在发送前准备好时间表),但在下一个答案中我分享了一些优化。问题还在于,当消费者的数量以非常不同的速度增长时,时间表会增长。第一个命题是如何以指数复杂性增长的?我敢肯定,对于m个基准和n个客户来说,这是O(mn)最坏的情况。正如我所说,第一个版本的主要目的是使用尽可能少的内存,这对嵌入式系统可能有用。您不太清楚应用程序是什么(“最佳性能(CPU、内存、IO)”非常模糊),因此我尝试为不同的优先级提供多个选项。是的,这是一个错误,我会解决它的复杂性是O(nm)(线性复杂性)。此解决方案的内存使用将变得非常有问题,特别是如果你的利率是素数。你知道吗
HCF(6,9) = 3 = IDLE MINIMUM PERIOD
LCM(6,9) = 18 = transmission cycle length
LCM/HCF = 6 = size of our schedule table
while(1) {
  sleep(IDLE_MINIMUM_PERIOD); // free CPU for idle min period
  i++; // initialized at 0
  send(ScheduleTable[i]);
  if (i == sizeof(ScheduleTable)) i=0;
}