Переместите единственное потоковое приложение в многопоточное, параллельное выполнение, моделирование Монте-Карло

Примеры:

class Dog 
{ 
public:
    barkAt( const std::string& pOtherDog ); // const reference
    barkAt( std::string pOtherDog ); // value
};

const & является обычно лучшим. Вы не подвергаетесь штраф разрушения и конструкция. Если ссылка не является константой, Ваш интерфейс предполагает, что это изменит переданный в данных.

8
задан svick 6 April 2016 в 18:53
поделиться

3 ответа

Для начала вам нужно понять, почему вы думаете, что использование нескольких потоков является оптимизацией, хотя на самом деле это не так. Использование нескольких потоков сделает вашу рабочую нагрузку быстрее , только , если у вас несколько процессоров, а затем, самое большее, во столько раз быстрее, чем у вас есть доступные ЦП (это называется ускорением ) . Работа не «оптимизирована» в традиционном понимании этого слова (т.е. объем работы не уменьшается - фактически, при многопоточности общий объем работы обычно растет из-за накладных расходов на потоки).

При разработке своего приложения вы должны найти части работы, которые можно выполнять параллельно или частично. Возможно, можно будет генерировать случайные числа параллельно (если несколько RNG работают на разных процессорах), но это также изменит результаты, поскольку вы получите разные случайные числа. Другой вариант - создать случайные числа на одном процессоре, а все остальное - на разных процессорах. Это может дать вам максимальное ускорение в 3 раза, так как ГСЧ по-прежнему будет работать последовательно и по-прежнему будет принимать 30% нагрузки.

Итак, если вы пойдете на такое распараллеливание, вы получите 3 потока: поток 1 запускает ГСЧ поток 2 производит нормальное распределение, а поток 3 выполняет остальную часть моделирования.

Для этой архитектуры архитектура производитель-потребитель является наиболее подходящей. Каждый поток будет читать свой ввод из очереди и передавать свой вывод в другую очередь. Каждая очередь должна быть блокируемой, поэтому, если поток ГСЧ отстает, поток нормализации будет автоматически блокироваться до тех пор, пока не станут доступны новые случайные числа. Для эффективности Я бы передавал случайные числа в массиве, скажем, 100 (или больше) по потокам, чтобы избежать синхронизации для каждого случайного числа.

Для этого подхода вам не нужны никакие дополнительные потоки. Просто используйте обычный класс потока, без пула или библиотеки. Единственное, что вам нужно (к сожалению) не в стандартной библиотеке, - это класс блокировки Queue (класс Queue в System.Collections не годится). Codeproject предоставляет разумно выглядящую реализацию одного; вероятно, есть и другие.

Единственное, что вам нужно (к сожалению) не в стандартной библиотеке, - это класс блокировки Queue (класс Queue в System.Collections не годится). Codeproject предоставляет разумно выглядящую реализацию одного; вероятно, есть и другие.

Единственное, что вам нужно (к сожалению) не в стандартной библиотеке, - это класс блокировки Queue (класс Queue в System.Collections не годится). Codeproject предоставляет разумно выглядящую реализацию одного; вероятно, есть и другие.

13
ответ дан 5 December 2019 в 12:11
поделиться

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

Библиотека параллельных расширений должна позволить вам распараллелить вашу программу, изменив некоторые ваших циклов for до Parallel.For циклов. Если вы хотите увидеть, как это работает, Андерс Хейлсберг и Джо Даффи предоставляют хорошее введение в своем 30-минутном видео здесь:

http://channel9.msdn.com/shows/Going+Deep/Programming-in-the- Age-of-Concurrency-Anders-Hejlsberg-and-Joe-Duffy-Concurrent-Programming-with /

ThreadPool против ThreadPool

ThreadPool, как следует из его названия, представляет собой пул потоков. Использование ThreadPool для получения ваших потоков имеет некоторые преимущества.

0
ответ дан 5 December 2019 в 12:11
поделиться

List определенно не является потокобезопасным. См. Раздел «безопасность потоков» в документации System.Collections.Generic.List . Причина в производительности: добавление безопасности потоков платно.

Ваша реализация случайных чисел также не является потокобезопасной; получение одних и тех же чисел несколько раз - это именно то, что вы ожидаете в этом случае. Давайте воспользуемся следующей упрощенной моделью rnd.NextUniform () , чтобы понять, что происходит:

  1. вычислить псевдослучайное число из текущее состояние объекта
  2. обновить состояние объекта, чтобы следующий вызов дает другое число
  3. возвращает псевдослучайное число

Теперь, если два потока выполняют этот метод параллельно, может произойти что-то вроде этого:

  • Поток A вычисляет случайное число как на шаге 1.
  • Поток B вычисляет случайное число как в шаге 1. Поток A еще не обновил состояние объекта, поэтому результат тот же.
  • Поток A обновляет состояние объект, как в шаге 2.
  • Поток B обновляет состояние объект, как на шаге 2, попирая состояние A изменения или может быть то же самое результат.

Как видите, любые аргументы, которые вы можете использовать, чтобы доказать, что rnd.NextUniform () больше не работает, потому что два потока мешают друг другу. Хуже того, подобные ошибки зависят от времени и могут лишь изредка проявляться как «сбои» при определенных рабочих нагрузках или в определенных системах. Кошмар отладки!

Одним из возможных решений является устранение разделения состояний: дать каждой задаче свой собственный генератор случайных чисел , инициализированный другим начальным числом (при условии, что экземпляры каким-либо образом не разделяют состояние через статические поля).

Другое (худшее) решение - создать поле, содержащее объект блокировки в вашем классе MersenneTwister , например:

private object lockObject = new object();

Затем используйте эту блокировку в своем MersenneTwister. Реализация NextUniform () :

public double NextUniform()
{
   lock(lockObject)
   {
      // original code here
   }
}

Это предотвратит параллельное выполнение метода NextUniform () двумя потоками. Проблема со списком в вашем Parallel.For может быть решена аналогичным образом: разделите вызов Simulate и вызов AddRange , а затем добавьте блокировку вокруг вызов AddRange .

Моя рекомендация: по возможности избегайте совместного использования любого изменяемого состояния (например, состояния RNG) между параллельными задачами. Если изменяемое состояние не используется совместно, проблем с потоками не возникает. Это также позволяет избежать блокировки узких мест: вы не хотите, чтобы ваши «параллельные» задачи ожидали одного генератора случайных чисел, который вообще не работает параллельно. Особенно, если 30% времени тратится на сбор случайных чисел.

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

1
ответ дан 5 December 2019 в 12:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: