Вопрос по Курсу: "Параллельное программирование с использованием MS VisualStudia 2010". При компиляции Самостоятельного задания (одновременная отрисовка прямоугольников, эллипсов и выдача в текст-бокс случайного числа) среда предупреждает: suspend - устаревшая команда; примените monitor, mutex и т.п. Создаётся впечатление, что Задание создано в более поздней среде, чем VS 2010. |
Томский политехнический университет
Опубликован: 23.01.2013 | Доступ: свободный | Студентов: 1157 / 192 | Длительность: 12:09:00
Тема: Программирование
Специальности: Программист, Архитектор программного обеспечения
Теги:
Самостоятельная работа 12:
Пример оптимизации параллельного приложения
< Лекция 13 || Самостоятельная работа 12
Аннотация: В данном практическом занятии будет рассмотрен пример оптимизации параллельного приложения с использованием Currency Visualizer.
- Создадим консольное приложение и назовем его, к примеру, "VisualizerConsoleApplication":
- Добавим в начало кода выражения: System.Threading и System.Threading.Task, для работы с потоками. В итоге должно получиться следующее:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace VisualizerConsoleApplication { class Program { static void Main(string[] args) { } } }
- После чего, добавим в класс Program два статичных метода:
- Метод DoSomething() в котором будет вызываться метод Thread.SpinWait();
- Метод ParallelLoop(), в котором перебираются числа от 0 до 1000 (созданные с помощью метода Enumerable.Range()) c использованием цикла foreach. В цикле foreach вызовем метод DoSomething().
В итоге должно получиться следующее:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace VisualizerConsoleApplication { class Program { static void DoSomething() { Thread.SpinWait(int.MaxValue / 10); } static void ParallelLoop() { var numbers = Enumerable.Range(0, 1000); foreach (var number in numbers) { DoSomething(); } } static void Main(string[] args) { } } }
- Теперь необходимо вызвать из метода Main() - метод ParallelLoop(), в отдельном потоке. Код будет выглядеть следующим образом:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace VisualizerConsoleApplication { class Program { static void DoSomething() { Thread.SpinWait(int.MaxValue / 10); } static void ParallelLoop() { var numbers = Enumerable.Range(0, 1000); foreach (var number in numbers) { DoSomething(); } } static void Main(string[] args) { new Thread(new ThreadStart(ParallelLoop)).Start(); Console.ReadLine(); } } }
- Далее, необходимо запустить Currency Visualizer. Для этого в меню Debug выбрать пункт "Start Performance Analysis" или можно использовать сочетание клавиш Alt+F2 (Для использования Currency Visualizer, VIsual Studio 2010 должна быть запущена с правами администратора)
- Запустится окно мастера. В нем необходимо выбрать пункт Concurrency и выбрать в нем два пункта "Collect resource contention data" и "Visualize the behavior of a multithreaded application" и для продолжения работы мастера кнопку Next:
- Далее, выбираем проекты для анализа на производительность (если их несколько) и жмем кнопку "Next":
- На последнем шаге мастера, выберите пункт "Launch profiling after the wizard finish", после чего нажмите кнопку "Finish":
- После чего запустится окно программы (в нашем случае это консоль) и процесс профилирования. Остановите процесс профилирования приложения с помощью кнопки "Stop Profiling" через 10-15 секунд:
- После завершения процесса анализа производительности, отобразится окно с различными отчетами:
- Выберем из списка "Cores". Отобразиться график, который показывает дисбаланс нагрузки на логические ядра процессора:
- Для того что бы сбалансировать нагрузку на ядра процессора модифицируем метод ParallelLoop() - заменим цикл foreach на Parallel.ForEach:
static void ParallelLoop() { var numbers = Enumerable.Range(0, 1000); Parallel.ForEach(numbers, (number) => { DoSomething(); }); }
- Повторно запустим профилирование. На диаграмме видно, что теперь каждое ядро процессора имеет сбалансированную нагрузку, кроме того каждое ядро выполняет различные потоки:
- Выберите вкладку "CPU Utilization". На данном графике отображается использование процессора с работающим приложением (в нашем случае приложение использует 54%):
- Теперь, добавим в приложение два идентичных потока и один объект sync (который будет использоваться для синхронизации lock):
static void Main(string[] args) { new Thread(new ThreadStart(ParallelLoop)).Start(); object sync = new object(); new Thread(new ThreadStart(() => { lock (sync) { Thread.Sleep(2000); } })).Start(); new Thread(new ThreadStart(() => { lock (sync) { Thread.Sleep(2000); } })).Start(); Console.ReadLine(); }
Примечание: ключевое слово lock не позволит одному потоку войти в раздел кода в тот момент, когда в нем находится другой поток.
- Запустим повторно профилирование. После профилирования перейдем во вкладку "Threads":
На диаграмме можно видеть два зависимых потока, один из потоков запускается с сегмента синхронизации (Thread.Sleep(2000)).
Листинг кода программы:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace VisualizerConsoleApplication { class Program { static void DoSomething() { Thread.SpinWait(int.MaxValue / 10); } static void ParallelLoop() { var numbers = Enumerable.Range(0, 1000); Parallel.ForEach(numbers, (number) => { DoSomething(); }); } static void Main(string[] args) { new Thread(new ThreadStart(ParallelLoop)).Start(); object sync = new object(); new Thread(new ThreadStart(() => { lock (sync) { Thread.Sleep(2000); } })).Start(); new Thread(new ThreadStart(() => { lock (sync) { Thread.Sleep(2000); } })).Start(); Console.ReadLine(); } } }
< Лекция 13 || Самостоятельная работа 12