2009-08-22 5 views
6

Каково влияние использования памяти на использование множества итераторов в C#? Предположим, что программа, которая выполняет тысячи циклов foreach - каждый цикл выделяет временный объект в куче по вызову GetEnumerator? Является ли CLR какой-либо оптимизацией (например, распределение стека IEnumerator объектов)? Или это просто не достаточно важный вопрос, о котором даже беспокоиться?Использование итераторов памяти в C#

ответ

13

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

Если вы делаете сотни тысяч foreach петель, предположительно, вы делаете фактические в этих циклах. Это почти наверняка будет далеким более важным, чем сами итераторы.

Обратите внимание, что использование foreach над массивом (известно, что это массив во время компиляции, то есть) не использует IEnumerable<T> в любом случае - он использует прямую индексацию. Однако я бы не изменил свой код на основе этого.

Как и прежде, если вас беспокоит производительность, вам необходимо измерить его и профилировать. Узкие места почти никогда не ожидаются.

+13

Несмотря на то, что, как всегда, отличный совет Джон, наше тестирование производительности показало значительное влияние производительности от размещения кучи/мусора в итераторах в некоторых реальных сценариях. Вот почему перечислитель - это злой тип изменяемого значения, а не ссылочный тип. Но, конечно, ключевая фраза - «наше тестирование производительности» - как вы заметили, глупо принимать решения о производительности без данных. –

+2

Ах да, злой изменчивый 'List ' iterators - Кажется, я помню сообщение в новостной группе с тем, что выглядело как разумный фрагмент кода, но который дал некоторые очень странные результаты из-за именно этого дизайнерского решения :) Я отредактирую ответ во всяком случае, хотя ... одеяло утверждает, что «это неважно» редко бывает хорошей идеей. –

2

Часто компилятор может оптимизировать цикл foreach в простой цикл, который требует только индексной переменной в стеке (или в регистре процессора).

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

Несколько итераторов, которые являются классами, все еще довольно малы и быстры. Вы могли бы создать миллионы из них без заметного воздействия.

+0

Звучит здорово ... Быстрый вопрос о последующем: если я реализую интерфейс IEnumerable, по определению метод GetEnumerator возвращает IEnumerator. Если мой пользовательский итератор является структурой, я предполагаю, что он будет возвращен как объект в штучной упаковке? –

+6

Jen, _carefully_ посмотрите, как выглядит List , и вы увидите, как избежать бокса. Фокус в том, что * цикл foreach фактически не требует, чтобы GetEnumerator возвращал IEnumerator *. Пока он возвращает то, что имеет правильные свойства и методы, вы золотые. Таким образом, вы можете избежать бокса, имея публичный GetEnumerator, который возвращает структуру, и явный IEnumerable .GetEnumerator, который возвращает коробчатую структуру. –

+4

Но опять же, как говорит Джон, не делайте этого глупого, если у вас нет достоверных данных о том, что создание счетчиков в куче является основной причиной реальной проблемы производительности, зависящей от клиента. Типы изменяемых значений вызывают всевозможные странные проблемы, которые дорого отлаживаются и трудно понять. –

Смежные вопросы