队列的性能?期望有大量的 RAM 使用,但我却得到大量的 CPU 使用
Posted
技术标签:
【中文标题】队列的性能?期望有大量的 RAM 使用,但我却得到大量的 CPU 使用【英文标题】:Performance of Queue? Expecting to have heavy RAM usage but instead I get heavy CPU usage 【发布时间】:2016-08-16 22:06:37 【问题描述】:假设我正在以每秒 50 到 200 次的可变速率接收信号。我想将收到的每个信号的时间戳存储到队列中,以便在超过 1 周前收到信号时将其从队列中删除。
public Queue<long> myQueue = new Queue<long>();
public OnSignalReceived()
myQueue.Enqueue(DateTime.UtcNow.Ticks);
PurgeOldSignals();
public void PurgeOldSignals()
while (myQueue.Count > 0 && myQueue.Peek() < DateTime.UtcNow.AddDays(-7).Ticks)
myQueue.Dequeue();
有没有更有效的方法来做到这一点?这是我的实现,我希望利用大量内存(因为假设平均每秒 100 个信号,这意味着队列将在开始清除项目之前保存大约 6000 万个项目(!))作为交换由于 Enqueue()
和 Dequeue()
的 O(1) 时间而具有计算性能。
然而,经过测试,我注意到瓶颈是 CPU 而不是 RAM。事实上,RAM 几乎没有被吃光,但 CPU 使用率从未停止增加。这是运行大约 16 小时后的结果(显然与我的 7 天目标相去甚远)
有什么优化的建议吗?
编辑 1:
事实上,这样做的全部目的只是随时了解我在上周收到了多少信号(精确到实际秒数)。也许有更好的方法来实现这一点?
【问题讨论】:
一目了然,你说每秒会有100个信号(每10毫秒一个)。计算机应该以纳秒级的速度处理内存配置,因此从队列中取出 100 个信号只需要 100 纳秒(甚至不到一微秒)。这意味着处理器正在以极快的速度处理 while 循环,因此内存受到限制,无法疯狂增长。 最简单的做法可能是限制清理。让它每秒只触发一次或类似的效果。 也许ConcurrentQueue<T>
更适合读写器线程?
为什么要在内存中保存 7 天 的数据(无论是几百万)在开始处理之前??跨度>
如果您创建的队列的初始容量是预期的最大容量,会发生什么情况?事实上,你会做很多成长,这会变得越来越昂贵。
【参考方案1】:
对于给定的任务,我将创建 3600*24*7 整数的循环队列。每个整数都表示该秒内的事件数(一周内的每一秒)。它只需要几兆字节。在测量事件上,对应于实际秒数 (=now) 的整数将增加。对数组中的所有项目求和会很方便,只需在更改时更新它即可快速获得它。
public class History
protected int eventCount = 0;
protected int[] array;
protected readonly int _intervalLength_ms;
long actualTime = 0;
int actIndex = 0;
public History(int intervalLength_ms, int numberOfIntervals)
_intervalLength_ms = intervalLength_ms;
array = new int[numberOfIntervals];
public int EventCount
get
Update();
return eventCount;
public void InsertEvent()
Update();
array[actIndex]++;
eventCount++;
protected void Update()
long newTime = DateTime.Now.Ticks / 10000 / _intervalLength_ms;
while (newTime > actualTime && eventCount > 0)
actualTime++;
actIndex++;
if (actIndex >= array.Length)
actIndex = 0;
eventCount -= array[actIndex];
array[actIndex] = 0;
if (newTime > actualTime)
actualTime = newTime;
actIndex = (int)(actualTime % array.Length);
它将使用参数new History(1000, 3600*24*7)
构造。
【讨论】:
感谢您的贡献,会调查的:)【参考方案2】:我看到两个问题:
-
(次要)但 DateTime.Now 比 DateTime.UTCNow 贵得多,因此您可能希望将其排除在循环之外,而不是执行 6000 万次。
(主要)每次收到信号时,您都在循环数百万个项目。如果您只想清除 7 天前的信号,您应该每天只运行一次清除。
【讨论】:
速度并不是 OP 应该使用“UTCNow”的唯一原因。与夏令时更改相关的问题是另一个问题。 re 2:因为他可能随时需要最近 7 天的数据?每天做一次会有不同的行为。 谢谢,这个例子是伪代码,我突然想到了……我确实在使用 DateTime.UtcNow。对于第 2 点,我看不到我每次如何遍历整个队列?从我的角度来看,我只会在超过 1 周前的时间戳之前出队。另外我不能每天只运行一次,因为我需要队列始终是最新的,而不是每天刷新一次 @ibiza 不是整个队列,但仍在检查数百万个,因为以每秒 100 个信号的平均速率,收集一百万个记录需要不到一天的时间。您正在循环查看 7 天 的记录。 @itsme86 ?这是一个队列。先进先出。所以,每次他添加时,他都在看后面的那些......【参考方案3】:我怀疑这慢的原因是因为这个
如果 Count 已经等于容量,则 Queue 的容量为 通过自动重新分配内部数组来增加,并且 现有元素在新元素之前被复制到新数组中 被添加。
如果 Count 小于内部数组的容量, 此方法是 O(1) 操作。如果内部数组需要 重新分配以容纳新元素,此方法变为 O(n) 运算,其中 n 为 Count。
来自Queue<T>.Enqueue()
here 上的 MSDN 文档。 CPU 使用率与 n
成比例增加,因为您正在对 myQueue
执行 O(n) 操作。
然后解决方案是通过调用var myQueue = new Queue<long>(n);
分配您希望该程序立即使用的尽可能多的内存,然后您的代码将进行所需的更改并切换到高内存使用率而不是 CPU 使用率。
【讨论】:
默认容量为32,每次需要增长都会翻倍。 我想可能是……会做更多的测试,谢谢! @hatchet true,虽然从给定的代码来看它是唯一可能的候选者,但我想知道问题是否出在其他地方...... @ibiza 你确定你的 CPU 问题出在队列实现上,而不是你程序的其他地方吗?如果你的程序也死了怎么办?将这一切都保存在内存中并不理想,除非您不关心在程序关闭/崩溃时丢失数据。 如果这是问题所在,您会看到 CPU 使用率达到峰值,而不是持续增加。每次重新分配时,队列的大小都会翻倍。以每秒 100 个信号的速度,在 16 小时内他将产生大约 580 万个信号。 2 的下一个更高的幂是 2^23,即 840 万。因此,如果队列从 32 个项目 (2^5) 开始,那么在 16 小时内最多会有 18 次重新分配。这些将显示为峰值,而不是持续增加 CPU 使用率。以上是关于队列的性能?期望有大量的 RAM 使用,但我却得到大量的 CPU 使用的主要内容,如果未能解决你的问题,请参考以下文章