【问题标题】:Queue performance wise which is better implementation - Array or Linked list队列性能明智,这是更好的实现 - 数组或链表
【发布时间】:2011-07-03 22:30:11
【问题描述】:

当我必须插入很少的元素时,哪种方式可以更快地入队和出队,数组比链表好吗?

我需要插入一些元素,我必须从队列中删除并读取已删除的元素。 如果它是数组,我可能必须在每次删除元素时修改索引。插入和删除也可以同时发生。

下面的案例哪一个更好?

typedef struct{
    mylist list;
    struct mylistQ *next;
}mylistQ;

数组代码

 static mylist myListQ[QUEUESIZE+1];
int qLast = 0;

void enqueue_element(mylist qItem)
{
        myListQ[qLast] = qItem;
    qLast++;
}

mylist dequeue_element()
{
 retryq:
   if(qLast >0) {
    mylist qReturn = myListQ[0];  
    int i;
    for (i = 0; i < qLast - 1; i++){
        myListQ[i] = myListQ[i + 1];
    }
    qLast--; 
    return qReturn;
     }
   else {
    goto retryq;
    }
}

链接列表

 int qLast = 0;

mylistQ *headElement = NULL;   
mylistQ *tailElement = NULL;     

void enqueue_element(mylist *List)
{
    mylistQ *newnode;      
    newnode=(mylistQ*)av_malloc(sizeof(mylistQ));
    newnode->next=NULL;
    newnode->list=*List;
    qLast++;
    if(headElement==NULL && tailElement==NULL)
    {
        headElement=newnode;
        tailElement=newnode;
    }
    else
    {
        tailElement->next=newnode;
        tailElement=newnode;
    }
 }

mylist dequeue_element()
{
    mylistQ *delnode;      /* Node to be deleted */
    mylist Dellist;
    if(headElement==NULL && tailElement==NULL){
        LOg( "Queue is empty to delete any element");
        }
    else
    {
       Log("In dequeue_picture queue is not empty");
        delnode=headElement;
        headElement=headElement->next;
    if (!headElement){
        tailElement=NULL;
    }
        Dellist = delnode->list;
        av_free(delnode);
    qLast--;
    }
        return Dellist;
}

【问题讨论】:

  • CPU 缓存位置为王。数组非常很难被击败。
  • @Hans:数组很难被击败,除非你需要调整它们的大小,或者它们对于你的缓存行来说太大了。不过,大小合理的数组的链接列表非常棒。
  • 第二nmichaels。如果您希望它对小型和大型队列都快速,则最好使用数组的链表。

标签: c arrays linked-list deque


【解决方案1】:

这取决于您将执行多少操作以及数组版本的具体实现方式。

如果您执行的操作相对较少,即总共少于 1000 个左右的入队/出队,那么数组会更快,因为它在内存中是连续的。保持一个指向前面的指针和一个指向后面的指针,总是在后面加,在前面出队。

另一方面,即使列表不超过 30 个左右的元素,如果这必须持续很长时间,您将不会遇到任何数组大小调整问题,这是数组的潜在问题。

链表保证了出色的性能,数组大小调整时必须注意。

编辑: 正如@Hans Passant 所提到的,数组速度很快,因为它们具有 CPU 缓存局部性。只要您的阵列很小,您的硬件就会优化性能,以便与存储阵列相关的内存将保留在您的 L2 中。可能在寄存器中的索引。这真的很快。从您不需要很多元素的事实来看,在这种情况下,数组将是理想的。是的,当你移动元素时你必须修改索引,但这实际上是一个非常快的操作,因为如果你用优化构建代码,索引将存储在注册器中。

这里有一个问题,你提到你可能必须同时做入队和出队,你的意思是它是并行的,即多个线程访问内存?如果是这种情况,阵列仍然会更快,但您会看到性能下降 800 倍。为什么?因为处理器不再能够缓冲与芯片上的队列相关的内存,但它必须存储在主内存中。此外,您还冒着在线程之间创建竞争条件的风险。想象一下,如果一个线程试图出队,而另一个线程试图入队并且列表中只有一个元素,那么您可能会遇到灾难。无论哪种方式,如果此应用程序非常注重性能,请务必使用 NDEBUG 和 -O3 标志进行编译(假设为 gcc)。

第二次编辑: 查看代码,并在下面查看其他答案,我建议让您的数组代码更高效并将其转换为圆形数组,因为听起来您对元素数量有上限。附带说明一下,您当前的数组实现效率极低,每次删除时都向前复制队列的其余部分,这没有任何意义,只需增加一个指向“第一个”索引的 int 指针。

伪代码:

int ciruclarArray[SIZE];
int front = 0;
int back = 0;

void enqueue(int elem)
{
    circularArray[back] = elem;
    if(back < (circularArray.length - 1))
        back++;
    else
        back = 0;
    return elem;
}

int dequeue()
{
    int toReturn = circularArray[front];
    //do same check for incrementing as enqueue
    return toReturn;
}

只是不要忘记对正常的东西进行错误检查。

【讨论】:

  • 我认为标题中的“DeQueue”是指double-ended queues,虽然并不完全清楚。
  • 好东西。谢谢大师。
  • np,只要确保它足够大以容纳所有值,如果你不小心你会覆盖东西!并检查以确保指针不会交叉 ie 为空。
【解决方案2】:

如果要存储在队列中的元素总数有上限,请使用circular array。这消除了在不断向其末尾添加元素时调整数组大小的需要。

【讨论】:

    【解决方案3】:

    即使你有很多元素,数组实现也可能是最快的。为了获得灵感,我查看了 GCC 的 C++ dequeue。它将队列存储为数组数组。我不确定迭代器是否像循环缓冲区一样环绕。数组实现还具有快速随机访问,如果您以后需要它。

    【讨论】:

      猜你喜欢
      • 2014-07-21
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      • 2013-11-06
      • 2018-05-17
      • 1970-01-01
      • 2015-04-06
      • 1970-01-01
      相关资源
      最近更新 更多