Вы используете параллельные расширения?

Я надеюсь, что это не неправильное использование stackoverflow; Недавно я увидел здесь несколько замечательных вопросов о параллельных расширениях, и это вызвало у меня интерес.

Мой вопрос: используете ли вы параллельные расширения, и если да, то как?

Меня зовут Стивен Туб, я работаю в группе разработчиков платформы параллельных вычислений Microsoft. Мы группа, ответственная за параллельные расширения. Мне всегда интересно услышать о том, как разработчики используют параллельные расширения (например, Parallel.For, PLINQ, ConcurrentDictionary и т. Д.), О положительном опыте, который у вас был, о отрицательном опыте, который у вас был, о запросах функций на будущее и т. on.
Если вы хотите поделиться такой информацией, сделайте это здесь в качестве ответа на этот вопрос или лично мне по электронной почте на stoub at microsoft dot com.

Я очень жду вашего ответа.

Заранее спасибо!


person Stephen Toub    schedule 18.10.2010    source источник
comment
Поскольку на этот вопрос не может быть единственного правильного ответа, я боюсь, что он не подходит для StackOverflow. Поскольку вы находитесь в команде MS, я не буду голосовать за закрытие, но, вероятно, другие проголосуют. Возможно, вам повезет больше на programmers.stackexchange.com и meta.stackoverflow.com. Также вы можете пометить вопрос как вики-сайт сообщества, чтобы голоса не учитывались в пользу / против и с меньшей вероятностью закрылись.   -  person Samuel Neff    schedule 18.10.2010
comment
Ok. Спасибо, Сэм. Приносим извинения за неправильное использование сайта. -Стивен   -  person Stephen Toub    schedule 18.10.2010
comment
@Stephen Toub, 12 часов и ноль голосов до закрытия. Похоже, люди не против. Хотя и ответов нет.   -  person Samuel Neff    schedule 19.10.2010
comment
Вероятно, единственные, у кого есть знания, чтобы ответить, не видят этого ... Я все равно пойду с голосованием, чтобы закрыть, или перейти на мета или P.SE ... Также посетите чат C # ...   -  person jcolebrand    schedule 03.12.2010
comment
это было бы намного лучше с тегами .net ...   -  person Marc Gravell    schedule 14.12.2010
comment
определенно должна быть вики сообщества   -  person Pauli Østerø    schedule 14.12.2010
comment
@Stephen Toub кто-то использует его для параллельных сокетов: stackoverflow.com/questions/5834755/   -  person    schedule 17.11.2011


Ответы (5)


Я использую TPL для выполнения вложенных Parallel.ForEach вызовов. Поскольку я обращаюсь к словарям из этих вызовов, я должен использовать ConcurrentDictionary. Хотя это приятно, у меня есть несколько проблем:

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

  • Внутренние ForEach итерации превышают ConcurrentDictionary экземпляров, что заставило бы систему тратить большую часть своего времени на счетчики для словаря, если бы я не добавил кеш-счетчик.

  • Многие из моих ConcurrentDictionary экземпляров на самом деле являются наборами, но нет ConcurrentSet, поэтому мне пришлось реализовать свой собственный с ConcurrentDictionary.

  • ConcurrentDictionary не поддерживает синтаксис инициализации объекта, поэтому я не могу сказать var dict = new ConcurrentDictionary<char, int> { { 'A', 65 } };, что также означает, что я не могу назначать ConcurrentDictionary литералы членам класса.

  • В некоторых местах мне нужно найти ключ в ConcurrentDictionary и вызвать дорогостоящую функцию для создания значения, если оно не существует. Было бы неплохо, если бы была перегрузка GetOrAdd, которая принимает addValueFactory, чтобы значение можно было вычислить, только если ключ не существует. Это можно смоделировать с помощью .AddOrUpdate(key, addValueFactory, (k, v) => v), но это добавляет накладные расходы на дополнительный вызов делегата для каждого поиска.

person Gabe    schedule 13.12.2010
comment
Я думаю, вам может быть лучше без вложенности. Нет причин создавать больше задач, чем есть ЦП - по крайней мере, не намного больше. Я бы рекомендовал избавиться от внутреннего Parallel.ForEach. - person Joe H; 14.12.2010
comment
Джо Х .: Внутренние на самом деле ускоряют процесс на 5-10%. - person Gabe; 14.12.2010

Я еще не использовал его широко, но я определенно внимательно следил за его использованием и искал возможности в нашей кодовой базе, чтобы использовать его (к сожалению, мы все еще привязаны к .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;
}
person Jesse C. Slicer    schedule 13.12.2010
comment
лол, тебе не нравится лямбда-выражения? ;) Во всяком случае, я пробовал этот код с электронной книгой «Властелин колец» в качестве входа. Он дает примерно те же результаты с AsParallel или без него, поэтому я думаю, что параллельные расширения на самом деле не помогают в этом случае ... Я также написал более короткую реализацию (в том же духе), которая почти в два раза быстрее, поэтому ваша реализация, вероятно, не самая быстрая и лаконичная реализация, которую только можно придумать;). Тем не менее, упражнение интересное ... - person Thomas Levesque; 14.12.2010
comment
Что ж, давай посмотрим, мужик! - person Jesse C. Slicer; 14.12.2010
comment
@Thomas - Вероятно, вы не получили уведомление о комментарии Джесси. Пусть этот комментарий будет таким уведомлением. - person Greg; 30.03.2011
comment
@Greg, я действительно не получил уведомления. Но в любом случае этого кода у меня больше нет, я написал его на LinqPad и не сохранил ... - person Thomas Levesque; 31.03.2011

Я использую его в своем проекте MetaSharp. У меня есть конвейер компиляции на основе MSBuild для DSL, а одноэтапные типы - это этап от многих ко многим. На этапе 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();
}
person justin.m.chase    schedule 13.12.2010
comment
Я бы порекомендовал this.Input.Skip(1).Any(), чтобы вы могли перестать считать после нажатия второго ввода. - person StriplingWarrior; 14.12.2010
comment
Значение: замените this.Input.Count () ›1 на this.Input.Skip (1) .Any (). - person dthorpe; 14.12.2010
comment
Хороший звонок! Это очень полезно. - person justin.m.chase; 16.12.2010

Мы не используем его широко, но он, безусловно, пригодился.

Мне удалось сократить время выполнения некоторых из наших более длительных модульных тестов примерно до 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));

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

person StriplingWarrior    schedule 13.12.2010

Я использую ConcurrentDictionary, в котором хранится более 100 миллионов элементов. В данный момент мое приложение использует около 8 ГБ памяти. Затем ConcurrentDictionary решает, что он хочет расти на другом Add. И он, по-видимому, хочет значительно вырасти (какой-то внутренний алгоритм Prima), поскольку у него заканчивается память. Это на x64 с 32 ГБ памяти.

Поэтому я хотел бы, чтобы логическое значение блокировало автоматическое повторное создание / повторное хеширование (параллельного) словаря. Затем я бы инициализировал словарь при создании с фиксированным набором сегментов (это не то же самое, что фиксированная емкость!). И со временем это станет немного медленнее, так как в ведре будет все больше и больше предметов. Но это предотвратит повторное хеширование и слишком быстрое и ненужное вымывание из памяти.

person IvoTops    schedule 26.06.2012