Нелинейное масштабирование операций.NET на многоядерной машине

Я не попробовал это (нашел его через Google). Мог бы работать? http://www.semdesigns.com/Products/Formatters/CSharpFormatter.html . Это довольно дешево на уровне 50 долларов США, но никакая доступная пробная версия.

8
задан spender 20 September 2009 в 00:32
поделиться

4 ответа

Итак, я наконец понял, в чем проблема - и думаю было бы полезно поделиться им с сообществом SO.

Вся проблема с нелинейной производительностью была результатом единственной строки внутри метода Evaluate () :

var coordMatrix = new long[100];

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

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

Эпилог: После того, как я внес это изменение, параллельный процесс смог выполнить 12,2 миллиона вычислений в секунду. .

PS Престижность Игорю Островскому за его актуальную ссылку на блоги MSDN, которая помогла мне идентифицировать и диагностировать проблему.

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

Взгляните на эту статью: http://blogs.msdn.com/pfxteam/archive/2008/08/12/8849984.aspx

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

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

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

  • Используете ли вы какие-либо разделяемые структуры данных (явно или неявно), требующие синхронизации?
  • Вы пробовали профилировать или записывать счетчики производительности, чтобы определить, где находится узкое место? Не могли бы вы дать еще какие-нибудь подсказки.

Edit: Извините, я только что заметил, что вы уже ответили на оба моих вопроса.

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

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

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

  • ЦП и ОС не могут посвящать все свое время вашему приложению. Таким образом, ему нужно время от времени переключать контекст, чтобы позволить другим процессам выполнить некоторую работу. Когда вы используете только одно ядро, маловероятно, что ваш процесс отключится, потому что у него есть три других ядра на выбор. Обратите внимание, что даже если вы можете подумать, что больше ничего не работает, ОС или некоторые службы все еще могут выполнять некоторую фоновую работу.
  • Если каждый из ваших потоков обращается к большому количеству данных, и эти данные не являются общими для потоков, вы скорее всего, не сможет сохранить все это в кеше ЦП. Это означает, что требуется гораздо больший доступ к памяти, что (относительно) медленно.

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

Следовательно, это может быть Лучшая идея разделить массив, предполагая, что время обработки каждого элемента, как ожидается, будет примерно одинаковым, независимо от положения элемента. Учитывая, что у вас есть 10 миллионов записей, это означает, что поток 1 должен работать с элементами от 0 до 2 499 999, поток 2 работает с элементами от 2 500 000 до 4 999 999 и т. Д. Вы можете назначить каждому потоку идентификатор и использовать его для вычисления фактического диапазона.

Еще одно небольшое улучшение - позволить основному потоку действовать как один из потоков, выполняющих вычисления. Однако, если я правильно помню, это очень второстепенная вещь.

3
ответ дан 5 December 2019 в 13:00
поделиться
Другие вопросы по тегам:

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