40 Возможности библиотеки Task Parallel Library. Визуализатор параллелизма

Задачи

  1. Выполните анализ загруженности вычислительной системы при разном режиме ожидания потока.
  2. Выполните анализ автоматического распараллеливания циклической обработки шаблоном Parallel.For.
  3. Выполните анализ распараллеливания PLINQ-запросов.

Методические указания

Среда разработки Visual Studio 12 содержит полезный инструмент для анализа эффективности выполнения параллельной программы – "Визуализатор параллелизма". Для запуска инструмента на вкладке "Анализ" запускаем "Визуализатор параллелизма" -> "Выполнить анализ с текущим процессом". Инструмент запускает программу и собирает информацию о её фактическом исполнении на данной вычислительной системе. Основные вкладки результатов: "Использование", "Потоки", "Ядра".


Использование ЦП

Вкладка содержит информацию об общей загрузке ЦП приложением в течение всего интервала выполнения.



Потоки

Вкладка "Потоки" выводит информацию о выполнении потоков, как пользовательских, так и рабочих потоков пула. Если в программе не используются объекты ThreadPool, Parallel, Task, то рабочие потоки простаивают. Для удобства восприятия информацию об отдельных потоках, например, простаивающих, можно скрыть. График "Сводка по потокам" суммирует время нахождения потоков в том или ином состоянии.



Ядра

Вкладка "Ядра" позволяет проанализировать перемещение потоков по фактическим исполнителям - ядрам процессора.



Анализ блокировки потоков

Применение "Визуализатора" позволяет исследовать особенности разных средств синхронизации.

Следующие фрагменты осуществляют блокировку основного потока с помощью цикла ожидания (активная блокировка) и с помощью встроенного механизма Join с выгружением контекста потока.

// Фрагмент 1 
 Thread t = new Thread(() => {  
  for (int i=0; i<10000; i++) 
   for(int j =0; j < 100; j++) 
    a[i] += Math.Sin(j) + i; 
  }); 
  
t.Start(); 
// Цикл ожидания 
 while(t.IsAlive) ; 
  
 // Фрагмент 2 
 Thread t = new Thread(() => { 
  for (int i=0; i<10000; i++) 
   for(int j =0; j < 100; j++) 
    a[i] += Math.Sin(j) + i; 
  
  }); 
 t.Start(); 
 t.Join(); 

Окно "Использование ЦП" иллюстрирует основной недостаток циклической блокировки – полезную работу осуществляет один поток, а вычислительные ресурсы заняты двумя потоками

Циклическая блокировка



Блокировка Join



При Join-блокировке основной поток №4808 почти все время находится в состоянии "Синхронизация", ожидая завершения потока №4848.



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



При блокировке основного потока методом Join, вычислительная система с двумя ядрами полностью предоставлена одному рабочему потоку. Операционная система активно перемещает поток с одного ядра на другое.





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

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

Блокировка SpinWait комбинирует два типа ожидания.

// Фрагмент 1 
 Thread t = new Thread(() => {  
  for (int i=0; i<10000; i++) 
   for(int j =0; j < 100; j++) 
    a[i] += Math.Sin(j) + i; 
  }); 
  
t.Start(); 
// Цикл ожидания 
 SpinWait.SpinUntil(() => !t.IsAlive); 
 



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

Анализ выполнения объектов библиотеки TPL

Применение объектов TPL для распараллеливания скрывает от программиста работу по созданию и синхронизации потоков. "Визуализатор параллелизма" позволяет получить основную информацию о действиях среды выполнения по распараллеливанию задач.

Метод Parallel.For автоматически распределяет итерации цикла по рабочим потокам. Вкладка "Потоки" раскрывает особенности выполнения параллельной циклической обработки.



Специальная строка System.Threading.Tasks указывает на вызов и работу объекта TPL. В окне "Сводка" отображается итоговая информация о выполнении цикла. Рассматривая работу потоков, видим, что в каждый момент времени работают только два потока, другие потоки вытесняются. Основной поток также участвует в обработке цикла. Сначала для обработки используются два дополнительных рабочих потока, спустя какое-то время в работу включается еще один поток.