队列的表现?期望有大量的RAM使用,但我的CPU使用量很高

时间:2016-08-16 22:06:37

标签: c# performance data-structures

让我们说我收到的信号是以每秒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个信号,这意味着在开始清除之前队列将容纳大约6千万个项目(!)由于O {1)到Enqueue()Dequeue()的时间,交换了计算性能。

然而,在测试之后,我注意到瓶颈是CPU而不是RAM。事实上,RAM几乎没有被吃掉,但CPU使用率从未停止增加。这是运行大约16个小时后的结果(显然远离我的7天目标)

enter image description here

有任何优化建议吗?

编辑1:

事实上,这个目的的全部目的只是随时了解我在上周获得了多少信号(精确到实际的秒数)。也许有更好的方法来实现这个目标?

3 个答案:

答案 0 :(得分:3)

对于给定的任务,我将制作3600 * 24 * 7整数的循环队列。每个整数都表示该秒中的事件数(一周内每秒)。它只需要几兆字节。在测量事件时,对应于实际秒(=现在)的整数将递增。在数组中包含所有项目的总和并在更改时更新它以便快速获取将非常方便。

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)构建。

答案 1 :(得分:-2)

我看到两个问题:

  1. (次要)但是DateTime.Now比DateTime.UTCNow贵得多,所以你可能想把它带出循环而不是做6000万次。
  2. (主要)每次收到信号时,您都会循环浏览数百万个项目。如果你只想用7天的信号清除它,你应该每天只进行一次清洗。

答案 2 :(得分:-2)

我怀疑这很慢的原因是因为这个

  

如果Count已经等于容量,则Queue的容量为   通过自动重新分配内部数组来增加   现有元素在新元素之前复制到新数组   被添加。

     

如果Count小于内部阵列的容量,   此方法是O(1)操作。如果需要内部数组   重新分配以容纳新元素,此方法成为一个   O(n)操作,其中n是Count。

来自Queue<T>.Enqueue() here上的MSDN文档。由于您正在n执行O(n)操作,因此CPU使用率正在增加myQueue

然后解决方案是通过调用var myQueue = new Queue<long>(n);来分配您希望此程序立即使用的内存,然后您的代码将进行所需的更改并切换到高内存使用而不是CPU使用。