加速System.Collection.Generic.Queue()可能吗?

时间:2012-04-03 18:09:13

标签: c#

我的模拟时间的50%用于以下代码:

    internal double simWithdrawalFast(double t)
    {
        simarrivals.Enqueue(t + Leadtime);
        return simarrivals.Dequeue();
    }

其中simarrivals是System.Collection.Generic.Queue<double>

如果我自己编写队列,它会更快吗?

编辑:注意队列中有两个开头,即当调用simwithdrawal时队列有大约200个元素。每次调用都会添加和删除元素。

3 个答案:

答案 0 :(得分:6)

这两种方法的实现对我来说似乎很简单:

public T Dequeue()
{
  if (this._size == 0)
    ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue);
  T obj = this._array[this._head];
  this._array[this._head] = default (T);
  this._head = (this._head + 1) % this._array.Length;
  --this._size;
  ++this._version;
  return obj;
}


public void Enqueue(T item)
{
  if (this._size == this._array.Length)
  {
    int capacity = (int) ((long) this._array.Length * 200L / 100L);
    if (capacity < this._array.Length + 4)
      capacity = this._array.Length + 4;
    this.SetCapacity(capacity);
  }
  this._array[this._tail] = item;
  this._tail = (this._tail + 1) % this._array.Length;
  ++this._size;
  ++this._version;
}

我很难相信代码的语义可以通过比这更有效的代码来实现。因此,您的问题可能处于更高的水平。为什么这个方法(simWithdrawalFast)被调用了这么多?您可能不得不找到一种方法来提高整体算法的效率,而不是找到更有效的数据结构。

如果问题是频繁调整队列大小,那么可能通过在构造队列时指定capacity来提高性能。我的预感是,这不是你的问题。

答案 1 :(得分:4)

Kirk的答案很棒。但是,如果你真的需要加快速度,你可以。

复制Kirk慷慨向您展示的功能,然后删除您不需要的支票或管理。例如,如果您发布的代码是对此队列的访问权限,则可以通过以下方式提高速度。

private double[] array; // Initialize this appropriately.

public double Dequeue()
{
  // Assume you use these functions properly, and avoid the exception checks.
  double obj = this._array[this._head];

  // No need to clean up after ourselves if used appropriately...
  //this._array[this._head] = default (T);

  // Still need to adjust the position in the queue.
  this._head = (this._head + 1) % this._array.Length;

  // May not need these either.
  //--this._size;
  //++this._version;
  return obj;
}


public void Enqueue(T item)
{
  // Don't bother trying to resize if you know you don't ever need to.

  this._array[this._tail] = item;
  this._tail = (this._tail + 1) % this._array.Length;

  // Probably don't need these.
  //++this._size;
  //++this._version;
}

此外,如果你真的想要挤出性能,看看你是否能找到一组稍快一点的操作来实现与头尾调整相同的目标。您还可以查看C#的unchecked关键字,看它是否适合您的情况。

答案 2 :(得分:1)

我想知道通过结合这些想法和内联可以实现什么。我没有在问题中明确说明队列的长度保持不变。所以我可以使用固定数组 simarrivalsArray,长度为simS:

以下调整使函数的速度提高了两倍(即函数花费的时间从50减少到33%):

internal double simWithdrawalFast(double time)
{
    double returnValue = simarrivalsArray[head]; //Withdrawal
    simarrivalsArray[head] = time+leadtime; //Enqueue
    head = (head + 1) % simS;//simS is length of array/queue, which stays constant;
    return returnValue;
}

现在疯狂的部分:摆脱模数符号会使在此功能中花费的时间比例下降到仅17%。这意味着该功能大约是原始功能的五倍,或者是上述版本的2.5倍(0.2 /(1 + 0.2)大约为0.17)。

internal double simWithdrawalFast(double time)
{
    double returnValue = simarrivalsArray[head]; //Withdrawal
    simarrivalsArray[head++] = time+leadtime; //Enqueue
    if(head==simS)//simS is length of array/queue, which stays constant;
    {
         head=0;
    }
    return returnValue;
}
编辑:为了回应HH,我写了一些代码来测试隔离的区别。

输出:

自己实施:7964
队列:23860

因子3,但当然在循环中花费了一些时间。用slimtune进行分析给了我 循环:23%
自己实施11%
队列:65%
即因子6.5。

我不认为这是测量开销,因为在分析时运行程序给了我 输出:
自己实施:8795
队列:27863

实现:

class Tester
{
    int simS;
    Queue<double> simarrivals;
    double[] simarrivalsArray;
    int head = 0;
    double leadtime = 5.0;

    internal Tester()
    {
        this.simS = 300;
        simarrivalsArray = new double[simS];
        simarrivals = new Queue<double>(simS + 1);
        for (int i = 0; i < simS; i++)
        {
            simarrivals.Enqueue(0.0);
        }            
    }

    internal void TestQueues()
    {
        System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
        double withdrawals = 5.0E+8;
        for (double d = 0.0; d < withdrawals; d += 1.0)
        {
            simWithdrawalFast(d);
        }
        Console.WriteLine("Own implementation:  "+ sw.ElapsedMilliseconds);
        System.Diagnostics.Stopwatch sw2 = System.Diagnostics.Stopwatch.StartNew();
        for (double d = 0.0; d < withdrawals; d += 1.0)
        {
            simWithdrawalFast2(d);
        }
        Console.WriteLine("Queue:  "+sw2.ElapsedMilliseconds);
    }
    double simWithdrawalFast(double time)
    {
        double returnValue = simarrivalsArray[head]; //Withdrawal 
        simarrivalsArray[head++] = time + leadtime; //Enqueue 
        if (head == simS)//simS is length of array/queue, which stays constant; 
        {
            head = 0;
        }
        return returnValue;
    }
    double simWithdrawalFast2(double t)
    {
        simarrivals.Enqueue(t + leadtime);
        return simarrivals.Dequeue();
    }
}