简体   繁体   English

在 C# 中迭代​​堆栈的最快方法

[英]Fastest way to iterate over a stack in c#

I feel that using GetEnumerator() and casting IEnumerator.Current is expensive.我觉得使用 GetEnumerator() 和转换 IEnumerator.Current 很昂贵。 Any better suggestions?有什么更好的建议吗?

I'm open to using a different data structure if it offers similiar capabilities with better performance.如果它提供类似的功能和更好的性能,我愿意使用不同的数据结构。

After thought:经过思考:
Would a generic stack be a better idea so that the cast isn't necessary?通用堆栈会是一个更好的主意,这样就不需要演员表了吗?

Stack<T> (with foreach) would indeed save the cast, but actually boxing isn't all that bad in the grand scheme of things. Stack<T> (使用 foreach)确实可以挽救演员阵容,但实际上拳击在宏伟计划中并不是那么糟糕 If you have performance issues, I doubt this is the area where you can add much value.如果您有性能问题,我怀疑这是您可以增加很多价值的领域。 Use a profiler, and focus on real problems - otherwise this is premature.使用分析器,并专注于实际问题 - 否则为时过早。

Note that if you only want to read the data once (ie you are happy to consume the stack), then this may be quicker (avoids the overhead of an enumerator);请注意,如果您只想读取一次数据(即您乐于使用堆栈),那么这可能会更快(避免枚举器的开销); YMMV.天啊。

    Stack<T> stack = null;
    while (stack.Count > 0)
    {
        T value = stack.Pop();
        // process value
    }

If you need the functionality of a Stack (as apposed to a List, or some other colleciton type), then yes, use a generic stack.如果您需要堆栈的功能(与列表或其他一些集合类型相关),那么是的,请使用通用堆栈。 This will speed things up a bit as the compiler will skip the casting at runtime (because it's garunteed at compile time).这将加快速度,因为编译器将在运行时跳过转换(因为它在编译时是 garunteed)。

Stack<MyClass> stacky = new Stack<MyClass>();

foreach (MyClass item in stacky)
{
    // this is as fast as you're going to get.
}

Have you done any benchmarks, or are they just gut feelings?您是否做过任何基准测试,或者它们只是直觉?

If you think that the majority of the processing time is spent looping through stacks you should benchmark it and make sure that that is the case.如果您认为大部分处理时间都花在遍历堆栈上,那么您应该对其进行基准测试并确保是这种情况。 If it is, you have a few options.如果是,您有几个选择。

  1. Redesign the code so that the looping isn't necessary重新设计代码,以便不需要循环
  2. Find a faster looping construct.找到一个更快的循环结构。 (I would recommend generics even though it wouldn't matter that much. Again, do benchmarks). (我会推荐泛型,即使它没有那么重要。再次,做基准测试)。

EDIT:编辑:

Examples of looping that might not be necessary are when you try to do lookups in a list or match two lists or similar.可能不需要的循环示例是当您尝试在列表中查找或匹配两个列表或类似内容时。 If the looping takes a long time, see if it make sense to put the lists into binary trees or hash maps.如果循环需要很长时间,看看将列表放入二叉树或哈希映射是否有意义。 There could be an initial cost of creating them, but if the code is redesigned you might get that back by having O(1) lookups later on.创建它们可能会产生初始成本,但如果重新设计代码,您可能会通过稍后进行 O(1) 查找来收回成本。

是的,使用通用堆栈将节省演员阵容。

Enumerating over a generic IEnumerable<T> or IEnumerator<T> doesn't create a cast if the iterating variable is of type T, so yes using the generic is going to be faster in most cases, but generics have some very subtle issues, especially when used with value types.如果迭代变量的类型为 T,则枚举泛型IEnumerable<T>IEnumerator<T>不会创建强制转换,所以是的,在大多数情况下使用泛型会更快,但泛型有一些非常微妙的问题,尤其是与值类型一起使用时。

Rico Mariani (Microsoft performance architect) has some posts detailing the differences and the underpinnings Rico Mariani(微软性能架构师)有一些帖子详细介绍了差异和基础

As far as speed is concerned there are multiple variables, depends on the context.就速度而言,有多个变量,取决于上下文。 For example, in a auto-memory-managed codebase like C#, you can get allocation spikes which can affect framerate in something like, say, a game.例如,在像 C# 这样的自动内存管理代码库中,您可能会遇到分配峰值,这会影响诸如游戏之类的帧率。 A nice optimization you can make for this instead of a foreach is an enumerator with a while loop:您可以为此进行的一个很好的优化而不是 foreach 是一个带有 while 循环的枚举器:

var enumerator = stack.GetEnumerator();

while(enumerator.MoveNext ()) {
  // do stuff with enumerator value using enumerator.Current
  enumerator.Current = blah
}

As far as CPU benchmarks, this probably isn't any faster than a foreach, but foreach can have unintended allocation spikes, which can ultimately "slow down" the performance of your application.就 CPU 基准测试而言,这可能并不比 foreach 快,但 foreach 可能会出现意外的分配峰值,这最终会“降低”应用程序的性能。

An alternative to creating an enumerator is to use the ToArray method, and then iterate over the array.创建枚举器的另一种方法是使用 ToArray 方法,然后遍历数组。 The stack iterator causes some slight overhead for checking whether the stack has been modified, whereas iteration over the array would be fast.堆栈迭代器在检查堆栈是否已被修改时会产生一些轻微的开销,而对数组的迭代会很快。 However, there is of course the overhead of creating the array in the first place.但是,首先当然有创建数组的开销。 As mats says, you should benchmark the alternatives.正如垫子所说,您应该对替代品进行基准测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM