Параллельное программирование с помощью языка C#

Знакомство с Визуализатором параллелизма в Visual Studio 12

Показывать лекцию целиком

Задачи

  • Выполните анализ загруженности вычислительной системы при разном режиме ожидания потока.
  • Выполните анализ автоматического распараллеливания циклической обработки шаблоном Parallel.For.
  • Выполните анализ распараллеливания 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. В окне "Сводка" отображается итоговая информация о выполнении цикла. Рассматривая работу потоков, видим, что в каждый момент времени работают только два потока, другие потоки вытесняются. Основной поток также участвует в обработке цикла. Сначала для обработки используются два дополнительных рабочих потока, спустя какое-то время в работу включается еще один поток.

    Вернуться к учебному плану