[.net] PLINQ
Вот статья, которая показывает легкость применения Parallel Extensions:
Ну че, прикольно, но есть же t++ штука, там похоже, но по-другому, но тоже можно прикольно распараллелить.
а какой-нибудь LinQ так распараллелить можно?
А у них есть какие-нибудь несколько более конкретные таблички и графички, какбэ показывающие, в какой момент затраты на организацию всей этой фигни начинают оправдываться? Я какую-то статью по диагонали просмотрел, ну да, в бонусы на рейтрейсере верю. А что в менее искуственных ситуациях?
Пишешь свой Enumerable и всё. Он не очень большой, кстати, я когда-то за пару дней вроде всё сделал и даже проверил на потыренных из моны тестах.
А у них есть какие-нибудь несколько более конкретные таблички и графички, какбэ показывающие, в какой момент затраты на организацию всей этой фигни начинают оправдываться?навскидку, как только у тебя появляется for который занимает заметное для пользователя время (а это обычно от нескольких секунд то имеет смысл этот for заменить на parrallel.For
Пишешь свой Enumerable и всё. Он не очень большой, кстати, я когда-то за пару дней вроде всё сделал и даже проверил на потыренных из моны тестах.а чем поможет свой IEnumerable? после первого же вызова - там же уже будет родной для linq enumerable.
Ты пишешь свой класс Enumerable, в котором Select использует этот самый параллельный форыч. И подключаешь его вместо System.Linq. После этого везде, где компилятору хочется вставить вызов Select, хоть напрямую, хоть после трансляции LINQ, вызывается твой метод. В чём проблема-то?
То есть проблема-то есть - поскольку внутри Select практически никогда нельзя узнать заранее, сколько элементов в последовательности, не очень понятно, как автоматически избавиться от дичайших тормозов на коротких последовательностях.
Плюс, вообще говоря на одно LINQ expression вполне достаточно одного параллелизирующего оператора, все остальные тупо лишние и должны работать как обычно.
Ты пишешь свой класс Enumerable, в котором Select использует этот самый параллельный форыч. И подключаешь его вместо System.Linq. После этого везде, где компилятору хочется вставить вызов Select, хоть напрямую, хоть после трансляции LINQ, вызывается твой метод. В чём проблема-то?чего?
что-то я не понял, чем мне этот свой IEnumerable поможет в следующей задаче:
window.Controls.Where(control => control.Name.Starts("a".OrderBy(control => control.Left).ThenBy(control => control.Width).Select(control => control.Height)
или предлагается писать так:
window.Controls.AsParallel.Where(control => control.Name.Starts("a".AsParallel.OrderBy(control=>control.Left).AsParallel.ThenBy(control => control.Width).AsParallel.Select(control=>control.Height)
где этот самый AsParallel будет возвращать этот самый новый придуманный IEnumerable
Ты пишешь свой класс Enumerable, в котором Select использует этот самый параллельный форыч. И подключаешь его вместо System.Linq.так все-таки пишешь свой IEnumerable, или пишешь свой Linq (т.е. свои Select, Where, OrderBy и т.д.)?
Есть интерфейс IEnumerable<T>.
Есть класс System.Linq.Enumerable, предоставляющий кучу экстенжен методов для этого интерфейса. Если ты вместо using System.Linq; пишешь using SuperPuperDuperLib.ParallelLinq; (в котором тоже есть какой-нибудь класс, предоставляющий все те же экстенжены, но параллелящие то все вызовы huita.Where(bla bla bla) ВНЕЗАПНО! оказываются параллельными.
Более того, все LINQ expressions, которые тупо транслируются в вызовы Where, Select, etc (даже не требуя того, чтобы это были экстенжены — совсем тупо транслируются!) тоже начинают работать параллельно.
В чём твой вопрос, собственно?
Ну да, свой класс Enumerable. За пару дней он пишется, особенно если рефлектором подглядывать, плюс есть множество (мощности минимум два) опенсорсных реализаций, не знаю, что у них с лицензиями, правда.
В чём твой вопрос, собственно?1. можно ли не переписывая полностью System.Linq.Enumerable - заюзать параллельность?
2. что именно и как надо переписать в Linq, чтобы заюзать параллельность?
Смотри, что получается в текущей схеме: есть например выражение result = something.Select(...).Where(...).Select(...). На самом деле оно означает вот что: result это IEnumerable, представляющее собой типа конвеер, каждый раз, когда ты вызываешь result.Next код исполняется задом наперёд — вначале последний Select вызывает Where, он вызывает первый Select, тот вызывает Next у something, данные начинают течь обратно, преобразовываясь и фильтруясь.
Куда здесь можно воткнуть распараллеливание? Самое простое — в тот итератор, которым обходится result, ну или в ToList который мы собираемся к нему применить. Но это неприкольно, потому что у нас есть Where, который пропускает существенно различные объёмы данных сквозь себя. Более того, обычно правильно написанная query возвращает очень мало результатов, как их параллелить-то?
Можно подумать о том, что будет, если параллелить в каждом из методов. Фигня получится, насколько я могу видеть. У нас всё-таки result.Next просит значения. Мы, конечно, можем в каждом из методов считать вперёд и запоминать, чтобы быстро вернуть, но это же отстой, разрушающий саму идею работы без промежуточных массивов и связанных с ними расходов.
Самое правильное было бы как-то параллелить с конца, с something как бы. Но control flow не в ту сторону течёт, не something пушает свои данные для обработки, а result их вытаскивает.
Плюс есть дополнительные сложности с методами вроде GroupBy, которые засасывают сразу все данные ниже в один хэштейбл и тем самым подрывают идею параллелизации на корню, имхо.
Так что не знаю. Не приспособлен LINQ для тупой параллелизации. Может быть если через IQueriable работать и натурально парсить лямбду, можно что-то сделать, а так - нет.
А как эти соображения соотносятся со словами MS о том, что PLINQ - обычная библиотека (не требующая спец поддержки языка/компилятора и что оно таки параллелит?
а так, что там не вызывается куча промежуточных методов выбора и фильтрации, когда собираемся выбрать след. элемент, а обрабатывая подсунутые лямбды и строя один большой метод. PLINQ делает тоже самое, только строит распараллеленую штуку.
Плюс есть дополнительные сложности с методами вроде GroupBy, которые засасывают сразу все данные ниже в один хэштейбл и тем самым подрывают идею параллелизации на корню, имхо.имхо, как раз именно эти функции и стоит параллелить
т.е. параллелить стоит:
GroupBy, OrderBy
можно попробовать еще ToDictionary, ToArray,
ps
основная проблема LinQ в том, что он работает с enumerable(последовательный доступ а для распараллеливания больше удобен индексный доступ
pps
с IQueryable решение, конечно, получится лучше, но там столько при реализации будет подводных камней
Оставить комментарий
elena670503
Летом вышел CTP Microsoft Parallel Extensions to the .NET Framework 3.5.Выглядит весьма интересно. В использовании для распараллеливания независимой обработки циклов проще, чем рихтеровская Power Threading Library. Чтобы заставить нормально работать рихтеровский AsyncResult вкупе с ThreadPool пришлось написать небольшую обертку и это только для того, чтобы распараллелить for. Для использования же майкрософтовской поделки также достаточно подключить одну ассемблю, но изменения в коде минимальны: for заменить на parallel.for. или в запросе from car in cars.AsParallel. Очень удобно.
Что скажете?