Самый быстрый способ перебора стека в c#

Я чувствую, что с помощью GetEnumerator () и кастинга IEnumerator.Ток дорогой. Есть предложения получше?

Я открыт для использования другой структуры данных, если она предлагает аналогичные возможности с лучшей производительностью.

после:
Будет ли общий стек лучшей идеей, чтобы литье не было необходимо?

7 ответов


вы сделали какие-либо тесты, или это просто чувства?

Если вы думаете, что большая часть времени обработки тратится на цикл через стеки, вы должны проверить его и убедиться, что это так. Если это так, у вас есть несколько вариантов.

  1. перепроектировать код так, чтобы цикл не был необходим
  2. найти быстрее циклы. (Я бы рекомендовал дженерики, хотя это не имело бы большого значения. Опять же стандарты.)

EDIT:

примеры цикла, которые могут не потребоваться, когда вы пытаетесь выполнить поиск в списке или сопоставить два списка или аналогичные. Если цикл занимает много времени, посмотрите, имеет ли смысл помещать списки в двоичные деревья или хэш-карты. Может быть начальная стоимость их создания, но если код переработан, вы можете получить его обратно, выполнив O(1) Поиск позже.


Stack<T> (С foreach) действительно спасет бросок, но на самом деле бокс не все это плохо в великой схеме вещей. Если у вас есть проблемы с производительностью, я сомневаюсь, что это та область, где вы можете добавить большую ценность. Используйте профилировщик и сосредоточьтесь на реальных проблемах-иначе это преждевременно.

обратите внимание, что если вы хотите прочитать данные только один раз (т. е. вы счастливы потреблять стек), то это мая быстрее (избегает накладных расходов перечислитель); YMMV.

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

Да, использование общего стека пощадит приведение.


Если вам нужна функциональность стека (как указано в списке или каком-либо другом типе colleciton), то да, используйте общий стек. Это немного ускорит процесс, так как компилятор пропустит кастинг во время выполнения (потому что он гарантирован во время компиляции).

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

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

перечисление над общим IEnumerable<T> или IEnumerator<T> не создает приведение, если итерационная переменная имеет тип T, поэтому да, использование универсального будет быстрее в большинстве случаев, но у дженериков есть некоторые очень тонкие проблемы, особенно при использовании с типами значений.

Рико Мариани (Microsoft performance architect) имеет некоторые сообщения, подробно описывающие различия и основы


что касается скорости, есть несколько переменных, зависит от контекста. Например, в кодовой базе с автоматической памятью, такой как C#, вы можете получить всплески выделения, которые могут повлиять на частоту кадров в чем-то вроде, скажем, игры. Хорошая оптимизация, которую вы можете сделать для этого вместо foreach, - это перечислитель с циклом while:

var enumerator = stack.GetEnumerator();

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

что касается тестов CPU, это, вероятно, не быстрее, чем foreach, но foreach может иметь непреднамеренные всплески распределения, которые могут в конечном счете" замедлить " производительность вашего приложения.


альтернативой созданию перечислителя является использование метода ToArray, а затем итерация по массиву. Итератор стека вызывает некоторые небольшие накладные расходы для проверки того, был ли стек изменен, тогда как итерация по массиву будет быстрой. Однако, конечно, есть накладные расходы на создание массива в первую очередь. Как говорит Матс, вы должны проверить альтернативы.