Вы используете параллельные расширения?
Я надеюсь, что это не злоупотребление stackoverflow; В последнее время я видел здесь несколько больших вопросов о параллельных расширениях, и это вызвало мой интерес.
Мой вопрос:
Вы используете параллельные расширения, и если да, то как?
Меня зовут Стивен Туб, и я нахожусь в команде Parallel Computing Platform в Microsoft. Мы являемся группой, ответственной за параллельные расширения. Мне всегда интересно узнать, как разработчики используют параллельные расширения (например, Parallel.For, PLINQ, ConcurrentDictionary и т.д.), Положительный опыт, который у вас был, отрицательный опыт, который у вас был, запросы функций на будущее, и поэтому на.
Если вы захотите поделиться такой информацией, пожалуйста, сделайте это, либо здесь, либо в качестве ответа на этот вопрос, либо мне конфиденциально по электронной почте в stoub at microsoft dot com
.
Я очень надеюсь услышать от вас.
Спасибо заранее!
Ответы
Ответ 1
Я использую TPL для выполнения вложенных вызовов Parallel.ForEach
. Поскольку я обращаюсь к словарям из этих вызовов, я должен использовать ConcurrentDictionary
. Хотя это хорошо, у меня есть несколько проблем:
-
Делегаты внутри ForEach
не выполняют много работы, поэтому я не получаю много parallelism. Кажется, что система проводит большую часть времени, соединяясь с потоками. Было бы неплохо, если бы был способ выяснить, почему он не улучшается concurrency и улучшает его.
-
Внутренние итерации ForEach
превышают экземпляры ConcurrentDictionary
, что приведет к тому, что система потратит большую часть своих счетчиков времени на словарь, если я не добавил кеш-перечислитель.
-
Многие из моих экземпляров ConcurrentDictionary
на самом деле являются наборами, но нет ConcurrentSet
, поэтому мне пришлось реализовать свои собственные с помощью ConcurrentDictionary
.
-
ConcurrentDictionary
не поддерживает синтаксис инициализации объекта, поэтому я не могу сказать var dict = new ConcurrentDictionary<char, int> { { 'A', 65 } };
, что также означает, что я не могу назначить литералы ConcurrentDictionary
для членов класса.
-
Есть несколько мест, где мне нужно найти ключ в ConcurrentDictionary
и вызвать дорогостоящую функцию для создания значения, если оно не существует. Было бы неплохо, если бы была перегрузка GetOrAdd
, которая принимает addValueFactory
, так что значение может быть вычислено только в том случае, если ключ не существует. Это можно моделировать с помощью .AddOrUpdate(key, addValueFactory, (k, v) => v)
, но это добавляет накладные расходы на дополнительный вызов делегата для каждого поиска.
Ответ 2
Я еще не использовал его еще много, но я определенно не обращал внимания на его использование и искал возможности в нашей базе кода, чтобы использовать его (к сожалению, мы .NET-2.0 связаны со многими из наши проекты все еще на данный момент). Один маленький камень, который я придумал, был уникальным счетчиком слов. Я думаю, что это самая быстрая и кратчайшая реализация, которую я могу придумать - если кто-то сможет сделать это лучше, это будет удивительно:
private static readonly char[] delimiters = { ' ', '.', ',', ';', '\'', '-', ':', '!', '?', '(', ')', '<', '>', '=', '*', '/', '[', ']', '{', '}', '\\', '"', '\r', '\n' };
private static readonly Func<string, string> theWord = Word;
private static readonly Func<IGrouping<string, string>, KeyValuePair<string, int>> theNewWordCount = NewWordCount;
private static readonly Func<KeyValuePair<string, int>, int> theCount = Count;
private static void Main(string[] args)
{
foreach (var wordCount in File.ReadAllText(args.Length > 0 ? args[0] : @"C:\DEV\CountUniqueWords\CountUniqueWords\Program.cs")
.Split(delimiters, StringSplitOptions.RemoveEmptyEntries)
.AsParallel()
.GroupBy(theWord, StringComparer.OrdinalIgnoreCase)
.Select(theNewWordCount)
.OrderByDescending(theCount))
{
Console.WriteLine(
"Word: \""
+ wordCount.Key
+ "\" Count: "
+ wordCount.Value);
}
Console.ReadLine();
}
private static string Word(string word)
{
return word;
}
private static KeyValuePair<string, int> NewWordCount(IGrouping<string, string> wordCount)
{
return new KeyValuePair<string, int>(
wordCount.Key,
wordCount.Count());
}
private static int Count(KeyValuePair<string, int> wordCount)
{
return wordCount.Value;
}
Ответ 3
Я использовал его в своем проекте MetaSharp. У меня есть компиляционный конвейер MSBuild для DSL, и один из этапов - это этап Many to Many. Этап M: M использует .AsParallel.ForAll(...).
Здесь фрагмент:
protected sealed override IEnumerable<IContext> Process()
{
if (this.Input.Count() > 1)
{
this.Input
.AsParallel<IContext>()
.ForAll(this.Process);
}
else if (this.Input.Any())
{
this.Process(this.Input.Single());
}
return this.Input.ToArray();
}
Ответ 4
Мы не используем его широко, но это, безусловно, пригодится.
Мне удалось сократить время выполнения нескольких наших более длительных модульных тестов примерно до 1/3 их первоначального времени, просто обернув некоторые из более длительных шагов при вызове Parallel.Invoke()
.
Мне также нравится использовать параллельные библиотеки для тестирования безопасности потоков. Я поймал и сообщил пару проблем с нитями с Ninject с кодом примерно так:
var repositoryTypes = from a in CoreAssemblies
from t in a.GetTypes()
where t.Name.EndsWith("Repository")
select t;
repositoryTypes.ToList().AsParallel().ForAll(
repositoryType => _kernel.Get(repositoryType));
В нашем фактическом производственном коде мы используем некоторые параллельные расширения для выполнения некоторых действий по интеграции, которые должны запускаться каждые несколько минут и которые состоят в основном из вытаскивания данных из веб-служб. Это особенно важно для parallelism из-за высокой задержки, присущей веб-соединениям, и позволяет нашим задачам завершить работу до того, как они снова начнут работать.
Ответ 5
Я использую ConcurrentDictionary, который хранит 100 миллионов + элементов. В настоящее время мое приложение использует около 8 ГБ памяти. Затем ConcurrentDictionary решает, что хочет расти на другом Add. И он хочет вырастить LOT, по-видимому (некоторый внутренний алгоритм prima), поскольку у него заканчивается память. Это на x64 с 32 ГБ памяти.
Поэтому я хотел бы, чтобы логическое значение блокировало автоматическое восстановление/повторное использование (параллельного) словаря. Затем я инициализировал словарь при создании с фиксированным набором ведер (это не то же самое, что фиксированная емкость!). И со временем это станет немного медленнее, так как в ведре все больше и больше предметов. Но это предотвратит повторное использование и выпадение памяти слишком быстро и без необходимости.