选择其中一个循环作为外循环有好处吗
Is there an advantage in choosing either loop as an outer loop?
我正在扩展现有的日志库。这是一个有两面性的系统:前端是任务将日志消息写入的地方,后端是应用程序可以插入侦听器的地方,侦听器将这些消息转发到不同的接收器。后端曾经是一个硬连接的侦听器,我现在扩展它以获得灵活性。该代码仅用于嵌入式设备,其中高性能(以每毫秒转发的字节数衡量)是一个非常重要的设计和实现目标。
出于性能原因,消息被缓冲,并且转发是在后台任务中完成的。该任务从队列中获取一块消息,对所有消息进行格式化,然后通过注册的函数将它们传递给侦听器。这些侦听器将筛选消息,并且只将那些通过筛选条件的消息写入其接收器。
考虑到这一点,我最终拥有了N
通知功能(侦听器)来向其发送M
消息,这是一个相当经典的N*M
问题。现在我有两种可能性:我可以循环消息,然后循环将消息传递给每个通知函数的通知函数。
for(m in formatted_messages)
for(n in notification_functions)
n(m);
void n(message)
{
if( filter(message) )
write(message);
}
或者我可以循环所有的通知功能,并将我拥有的所有消息一次性传递给它们:
for(n in notification_functions)
n(formatted_messages);
void n(messages)
{
for(m in messages)
if( filter(m) )
write(m);
}
关于哪个设计更有可能允许每个时间片处理更多的消息,是否有任何的基本考虑因素?(注意这个问题是如何决定监听器的接口的。这不是一个微观优化问题,而是一个关于如何做出不妨碍性能的设计的问题。我只能在很久以后进行测量,然后重新设计监听器接口将是昂贵的。)
我已经做了一些考虑:
- 这些侦听器需要在某个地方编写消息,这相当昂贵,因此函数调用本身在性能方面可能不太重要
- 在95%的情况下,只有一个听众
对于哪个设计更有可能允许每个时间片处理更高数量的消息,是否有任何基本考虑因素?
一般来说,这方面的主要考虑因素通常可以归结为两件主要的事情。
-
如果其中一个循环在可能具有良好内存局部性的对象上循环(例如在值数组上循环),则将该部分保留在内部循环中可能会将对象保留在CPU缓存中,并提高性能。
-
如果你计划尝试并行化操作,那么在外循环中保留"较大"(就计数而言)集合可以有效地并行化外循环,而不会导致线程的过度订阅等。在外级别并行化算法通常更简单、更干净,因此,如果以后有可能的话,在外循环设计具有潜在更大并行"块"工作的循环可以简化这一点。
这些侦听器需要在某个地方编写消息,这相当昂贵,因此函数调用本身在性能方面可能不太重要。
这可能会完全否定将一个循环移出另一个循环的任何好处。
在95%的情况下,只有一个听众。
如果是这种情况,我可能会将侦听器循环放在外部范围,除非您计划并行化此操作。考虑到这将在嵌入式设备上的后台线程中运行,并行化是不可能的,因此将侦听器循环作为外循环应该会减少总指令数(它实际上变成了M个操作上的循环,而不是单个操作上的M个循环)。
循环的顺序可能比侦听器签名的变化优势小得多(注意,无论哪个循环在外部,侦听器都可以维护第一个接口,即两个循环都可以在调用者中)。
第二个接口(即向每个侦听器发送一系列消息)的自然优势在于,您可以对侦听器的实现进行可能的分组。例如,如果向设备写入,侦听器可以将多个消息打包到单个write
中,而如果接口接收单个消息,则侦听器缓存(这需要内存和cpu成本),或者每次调用需要执行多个writes
。
因此,以下几个因素将在其中发挥作用:
缓存中的消息距离有多近,它们占用了多少空间?如果它们相对较小(几KB或更少)并且靠近(例如,在进行大量其他内存分配的系统中,不是一个间隔几秒分配内存的链表)。
如果它们很近,而且很小,那么我相信第二种选择更有效,因为消息将被预取/缓存在一起,其中调用所有n
侦听器和筛选器函数(也假设有很多函数,而不是一个、两个或三个)可能会导致前一消息的更多"缓存丢弃"。当然,这也取决于侦听器和筛选器函数的实际复杂程度。他们做多少工作?如果每个函数都做了相当多的工作,那么你按哪个顺序做可能就不那么重要了,因为它只是边际的。
没有任何"根本"原因可以解释为什么一个设计比另一个更好。根据库的使用方式,可能会出现一些非常小的速度差异。我个人更喜欢先迭代监听器,然后再迭代消息。
我猜处理程序的主体通常都很快。您可能希望将侦听器作为外循环进行迭代,以便重复调用相同的代码。像间接呼叫预测这样的东西用这种方式会更好。当然,数据缓存的使用情况会更糟,但希望每个消息缓冲区都足够小,可以轻松地放入L1。
为什么不让听众接受const vector<message> &
并让他们自己进行迭代呢?他们可以做任何有益的缓冲,最后只做一次昂贵的写入。
- 我试图制作一个程序,要求用户输入问题和答案,但程序循环不正确
- C++-字符串是否包含一个带有简单循环的单词
- 基于范围的 for 循环:迭代使用一个元素扩展的向量
- 我可以创建一个包含两个变量的 for 循环,但时间复杂度仍然为 O(n) 吗?
- 使用一个循环与两个循环
- 用于在 C++ 中使用 while 循环查找下一个素数的简单函数
- 使用一个 for 循环如何迭代和测试迭代器值?
- 我正在尝试创建一个菜单,但我的代码一直在循环
- 转到基于范围的 for 循环中的下一个迭代器
- C++:返回一个基于范围 for 循环迭代器,其中包含继承对象
- 如何循环 n 次,在 xml 文件中每个循环一个级别
- 初始化时 num 的哪个值会给这个嵌套循环一个"true"输出?
- 两次相同的 for 循环:一个编译,另一个不编译
- 如何在C 中循环一个语句
- 循环一个级数
- 如何在c++中循环一个函数
- 循环一个精灵以显示不止一次
- C++循环一个菜单,直到选择了所有选项
- QT:如何每秒循环一个方法?C++
- 循环一个mpl::map