Фильтрация IEnumerable Pattern - PullRequest
       15

Фильтрация IEnumerable Pattern

2 голосов
/ 03 февраля 2010

Рассмотрим следующий простой шаблон кода:

foreach(Item item in itemList)
{
   if(item.Foo)
   {
      DoStuff(item);
   }
}

Если я хочу распараллелить его с помощью Parallel Extensions (PE), я мог бы просто заменить конструкцию цикла for следующим образом:

Parallel.ForEach(itemList, delegate(Item item)
{
   if(item.Foo)
   {
      DoStuff(item);
   }
});

Однако PE выполнит ненужную работу, назначая работу темам, для которых Foo оказался ложным.Таким образом, я думал, что промежуточный упаковщик / фильтрация IEnumerable может быть разумным подходом здесь.Ты согласен?Если так, какой самый простой способ достичь этого?(Кстати, в настоящее время я использую C # 2, поэтому я был бы признателен, по крайней мере, за один пример, который не использует лямбда-выражения и т. Д.)

Ответы [ 3 ]

4 голосов
/ 03 февраля 2010

Я не уверен, как работает разбиение в PE для .NET 2, поэтому трудно сказать там. Если каждый элемент помещается в отдельный рабочий элемент (что было бы довольно плохой стратегией разделения), то фильтрация заранее имеет смысл.

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

Кроме того, в .NET 4 стратегия разделения, используемая TPL, справится с этим достаточно хорошо. Он был специально разработан для обработки ситуаций с различными уровнями работы. Он выполняет разбиение на «чанки», поэтому один элемент не отправляется одному потоку, а поток получает назначенный набор элементов, которые он обрабатывает навалом. В зависимости от частоты ложного item.Foo паралелизация (с использованием TPL) вполне может быть быстрее, чем предварительная фильтрация.

1 голос
/ 03 февраля 2010

Это все факторы вплоть до этой единственной строки:

Parallel.ForEach(itemList.Where(i => i.Foo), DoStuff);

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

Для .Net 2.0 я думаю вы можете сделать это следующим образом (мне немного непонятно, что передача имен методов в качестве делегатов все равно будет работать, но я думаю, что это будет):

public IEnumerable<T> Where(IEnumerable<T> source, Predicate<T> predicate)
{
   foreach(T item in source)
       if (predicate(item))
          yield return item;
}

public bool HasFoo(Item item) { return item.Foo; }

Parallel.ForEach(Where(itemList, HasFoo), DoStuff);
1 голос
/ 03 февраля 2010

Если бы я реализовал это, я бы просто отфильтровал список перед вызовом foreach.

var actionableResults = from x in ItemList WHERE x.Foo select x;

Это отфильтрует список, чтобы получить элементы, на которые можно воздействовать.

ПРИМЕЧАНИЕ: это может быть преждевременная оптимизация, которая не может существенно повлиять на вашу производительность.

...