【问题标题】:Most efficient way to dequeue bytes from Queue in C#在 C# 中从队列中取出字节的最有效方法
【发布时间】:2011-11-09 11:02:30
【问题描述】:

我有一个包含一些数据的队列。如果我想检索 5 个字节(或者如果少于 5 个,则尽可能多),以下哪种方法更有效,或者没有区别?

选项 1:

        byte[] ret = null;

        if (this.dataQueue.Count >= numBytes)
        {
            ret = new byte[numBytes];
        }
        else
        {
            ret = new byte[this.dataQueue.Count];
        }

        for (int i = 0; i < ret.Length; i++)
        {
            ret[i] = this.dataQueue.Dequeue();
        }

        return ret;

选项 2:

        List<byte> l = new List<byte>();

        try
        {
            for (int i = 0; i < numBytes; i++)
            {
                l.Add(this.dataQueue.Dequeue());
            }
        }
        catch
        {
            System.Diagnostics.Debug.WriteLine(string.Format("Unable to retrieve requested number of bytes from queue. ({0} bytes requested, {1} bytes available)", numBytes, l.Count));
        }

        return l.ToArray();

提前感谢您的任何建议。

【问题讨论】:

  • 你做过测试吗?
  • 您是否真的遇到了要解决的性能问题?如果是,您是否认为这是瓶颈?
  • 当您说“队列”时,您具体指的是什么?您使用的是哪种数据结构?
  • 我还没有进行任何测试。我没有看到任何瓶颈。我刚刚意识到有两种方法可以执行相同的任务,并且比什么都好奇。我正在使用 System.Collections.Generic.Queue

标签: c# list queue


【解决方案1】:

性能确实取决于您的场景,并且总是有人会警告您不要过早优化并在假设某些东西很慢之前找到瓶颈等等,然后还有可能或可能的角度的假设不成立;我真的同意前一部分,但不喜欢草率下结论,并认为程序的某些部分被证明是较慢的,因此需要优化这样的部分。

无论如何,请考虑以下原始的、不完全实用的临时程序(使用您的方法,在此处命名为 DequeueTryDequeue,但未列出):

var data = new byte[4096];
var random = new Random();      
var queue = new Queue<byte>();
var stopWatch = new Stopwatch();

TimeSpan dequeueTimespan, tryDequeueTimespan;

stopWatch.Restart();
for (int i = 0; i < 1000000; i++)
{
    random.NextBytes(data);
    foreach (var item in data)
    {
        queue.Enqueue(item);
    }
    Dequeue(queue, 1024);
    queue.Clear();
}
stopWatch.Stop();
dequeueTimespan = stopWatch.Elapsed;

stopWatch.Restart();
for (int i = 0; i < 1000000; i++)
{
    random.NextBytes(data);
    foreach (var item in data)
    {
        queue.Enqueue(item);
    }
    TryDequeue(queue, 1024);
    queue.Clear();
}
stopWatch.Stop();
tryDequeueTimespan = stopWatch.Elapsed;

Console.WriteLine("Dequeue:    {0}", dequeueTimespan);
Console.WriteLine("TryDequeue: {0}", tryDequeueTimespan);
Console.ReadKey();

现在,如果我们对每个迭代运行超过 10000 次,我们会得到:

Dequeue:    00:00:12.6178244
TryDequeue: 00:00:13.6747715

对于每个方法 1000000 次迭代:

Dequeue:    00:02:16.4144764
TryDequeue: 00:02:13.2039597

当然,我们在这里做其他的工作,但这都是相对的;我们在这里也夸大了你的情况,因为你只需要 5 个字节,所以......

再次10000次迭代的第一种情况:

Dequeue:    00:00:10.5624014
TryDequeue: 00:00:10.2529997

我并不是说这些是完美的结果,但它们结果;但重点是这个。

两者之间的性能下降可以忽略不计。

在实际环境中还有许多其他因素需要考虑,不用说,也不要在这里尝试列出所有变量,您将(或变得)意识到并应该花时间与您的场景。

【讨论】:

  • 您好,感谢您如此详细的回复!我期待更大的差异,因为我不确定List&lt;&gt; 是否预先分配空间,或者是否需要对每个Add 执行大量工作。我还想知道try/catch 块是否昂贵。但在如此微不足道的差异下,是否有任何一种可以被认为是更好的代码?
  • 还要记住分配相对便宜,但是如果你创建了大量垃圾,你的回收周期可能会更长
【解决方案2】:

不要误会,但你问的是一个几乎不可能的问题。在处理如此小的差异时,您需要更多信息才能做出裁决。

例如,进行了哪些优化,以及将在哪种架构上运行。 此外,当前的程序状态和流程是什么(内存紧张吗?CPU 是否以接近 100% 的速度工作?),以及缓存(应用程序\操作系统\硬件)如何。

这个列表不完整,离它很远,但它应该通过这一点 - 如果您正在处理精确的优化和性能,您将需要更多详细信息(不仅仅是为了我们,但对于任何可能对答案有想法的人)。

如果你能详细说明你的问题,那就太好了。

编辑:

请注意,这两种方法以相同的方式使队列出队(调用 Dequeue() 的 for 循环),因此在这方面并没有什么不同,而是它们收集从队列中出队的项目的方式不同。队列。如果问题是最快的方法是什么(使用出队的项目),那根本不是收集它们并将它们发送到他们的流程中。如果要退回项目,那么您将返回原始问题,但有一个可能对您有所帮助的子问题 - 您希望在项目出队后对项目集合进行什么处理。

希望对你有帮助

【讨论】:

  • 您好,感谢您的回复。我没有考虑所有这些因素,因为我假设无论什么平台,都会有一种方法相对于另一种更有效。我的问题更多是出于好奇,因为我希望库非常便携,因此不应该高度依赖这些细节。如果这使得无法选择更好的解决方案,那么不用担心。感谢您的帮助。
  • 在这种情况下,我只需要注意这些方法在使项目出队的方式上没有区别(在大多数方面)。我已经编辑了回放来解释
  • 感谢您的更新。返回的字节作为字节数组发送到另一个库,因此最后需要以这种格式。
  • 如果它们最终要用作数组,您最好从数组开始(如果手头有性能问题)
  • 有道理。我想了很多,但不确定这是否有很大的不同——或者实际上它是否在每种方法的幕后都做了类似的工作。感谢您的澄清。
【解决方案3】:

第一个,因为它做了1次分配,而

l.Add(this.dataQueue.Dequeue()) 

需要重新分配列表所需的空间几次,除非你预先保留它:

l.Capacity = 5

这不会影响您的列表长度,它只是为元素保留内存空间(我们知道您最多会有 5 个)。

当然,检查您是否真的需要在队列中有字节并在每次需要获取一个字节时调用 Dequeue 也是值得的。

【讨论】:

  • 感谢您的回复。我以前从未见过Reserve,在网上找不到任何参考资料。你有这个方法的文档链接吗?最初问这个问题的原因之一是我不确定 List 是否每次都需要重新分配空间,或者它是否更智能,因此在创建时会占用一定数量的空间供使用(因为您可以指定一个容量)。所以感谢您澄清这一点。
  • 抱歉,我将它与另一种语言/课程混淆了。它是容量属性。将更新我原来的答案。
  • 奇怪。我刚刚回复了,但是它已经消失了,如果您收到两次,很抱歉!但感谢您的更新。这更有意义。那么如果容量已经足够大了,这是否还有显着的效果呢?
  • 应该差不多,除非你调用 Add 很多,超过五次:)
猜你喜欢
  • 1970-01-01
  • 2012-11-08
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 1970-01-01
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
相关资源
最近更新 更多