ПАРАЛЛЕЛЬНЫЙ ОТЛАДКИ

Отладка параллельных приложений, основанных на задачах, в Visual Studio 2010

Даниэл Мот и Стивен Тауб

Загрузить образец кода

Попросите производителю любой ЦП и графического ПРОЦЕССОРА и они будут сообщают, что в будущем многоядерных. Основные скорости процессора не увеличение экспоненциальное скоростью последние четыре десятилетий;Вместо этого новые компьютеры создаются с помощью нескольких ядер. В результате «бесплатный»Повышение производительности, разработчики приложений на год за годом исчезли. Чтобы получить бесплатный завтрак, предлагаемые лучше и лучше оборудования--и улучшения приложений с функциями производительности с учетом нового--необходимо преимущества нескольких ядер через параллелизма.

В Visual C++ 10 и средой Microsoft .NET Framework 4 и доступны с помощью Visual Studio 2010, Майкрософт введение новых библиотек и поддержке сред значительно упростить процесс выражение параллелизма в коде базового вместе с новым средством анализа производительности и отладке параллельных приложений. В этой статье вы узнаете о поддержке процесса отладки в Visual Studio 2010, которая в основном фокусируется на моделях программирования, основанных на задачах.

Потребность в основе задачи программирования

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

Простой схемы включать статические секционирования: Разделите работу в фиксированное число единиц фиксированного размера. Конечно не требуется, чтобы написать код для каждой конфигурации оборудования, оно будет выполняться на и predetermining фиксированное число единиц заранее inhibits масштабируемость приложения, как выполняется на крупных и более компьютеров. Вместо этого можно выбрать число единиц, динамически во время выполнения в зависимости от сведений о компьютере. Например секции работу в одну единицу на основной. Таким образом, если все подразделения равны размер в терминах времени обработки требуют и при использовании одного потока на единицу вы сможете saturate на компьютере.

Однако этот подход по-прежнему остается много быть необходимости. Редко, можно разделить реальной рабочей нагрузки таким образом, каждая единица гарантированно занять же объем времени, особенно когда необходимо учитывать внешних факторов учетной записи, таких как другие рабочие нагрузки, может запущена на компьютере одновременно и использование некоторых ресурсов на компьютере. В таких случаях будет секционирования одной единицы на основные вероятно итоге неравномерно распределение работы: Некоторые потоки будет завершить их единиц перед другими пользователями, приводя дисбаланс нагрузки и некоторые ядер будут сидеть простоя, во время завершения других. Для решения этой проблемы нужно over-partition работы, разделение рабочей нагрузки на наименьший подходящего единицы, таким образом, чтобы все ресурсы компьютера partake обработки рабочей нагрузки до завершения.

Если выполнение единицу работы, произведенных ноль издержки, просто предложенный решением было бы идеально, но очень немногие реальные операции вызывают ноль издержки. Исторически потоков были механизм для выполнения таких единица работы: создать поток для каждой единицы работы, позвольте ей выполнить и затем разорвать работы потока. К сожалению, потоки являются относительно большой вес и издержки с помощью таким способом можно запретить тип из over-partitioning мы описал. Что необходимо — это механизм lighter вес для выполнения этих секционированные единиц для минимизации издержек — механизм, который позволит over-partition guilt меньше. Этот подход, вместо создания одного потока на единицу может использовать планировщик, планирует отдельных единиц для выполнения в потоках, он управляет, хранение максимально сократить число единиц, по-прежнему обеспечивая максимальную производительность.

Что мы просто описал это пул потоков, amortizes затраты на управление потоками через все рабочие элементы по расписанию, таким образом сводя к минимуму дополнительные издержки, связанные с элементом отдельные рабочие. В Windows пул потоков доступна через функцию QueueUserWorkItem, экспортированных из Kernel32.dll. (Windows Vista представила новый поток пула также функциональные возможности.) В .NET Framework 4 таких пул доступен через класс System.Threading.ThreadPool.

Вы вышеупомянутых API дают развернутого с относительно минимальными издержками, они во многом нацелено на «срабатывание и забыл»работа. Например класс .NET Framework 4 ThreadPool не предоставлять любые согласованный механизм обработка исключений, для отмены работы, ожидания работы для выполнения, получение уведомлений при завершения работы и т.д. Эти пропуски заполняются новый API в .NET Framework 4 и 10 Visual C++, предназначенных для «на основе задач»При программировании управляемого и машинного кода. Задачи представляют единиц трудозатрат, которые могут быть выполнены эффективно базовой диспетчером при по-прежнему предоставление богатые функциональные возможности для работы с и управление аспектов их выполнения. В Visual C++ 10 эти интерфейсы центрируются по Concurrency::task_group и Concurrency::task_handle типов. В .NET Framework 4 они центрируются по новый класс System.Threading.Tasks.Task.



Рисунок 1 параллельных окно стеки



Рис. 2 Параллельных окна задачи

Сегодня отладки в Visual Studio

Журнал разработки программного обеспечения продемонстрированы времени и время снова модели программирования значительно выиграть от exemplary поддержку отладки, и в этом отношении доставляет Visual Studio 2010, предоставляя два новых отладки окна средства для помощи параллельного программирования на основе задач. Но прежде чем мы рассмотрим эти новые возможности, давайте рассмотрим процесс отладки в Visual Studio сегодня установить на стадии.

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

Точку входа для отладки процесса в Visual Studio конечно, присоединение отладчика. Это происходит по умолчанию при нажатии клавиши F5 (эквивалент Выбор Debug >Запустите отладку команды) в проекте, открыт в Visual Studio. Можно также вручную присоединить отладчик к процессу, выбрав Debug >Присоедините к обработать команду. После того как отладчик присоединен, следующим шагом является переключиться в режим отладчика. Это может произойти несколькими способами, включая обращения пользовательские точки останова, вручную критическое (через Debug >Разорвать все команды), процесс запрашивает его (например, в управляемый код через вызов метода System.Diagnostics.Debugger.Break) или даже когда создается исключение.

На рис. 3 блоков поиск

static void Main(string[] args)
{
var primes =
from n in Enumerable.Range(1,10000000)
.AsParallel()
.AsOrdered()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
where IsPrime(n)
select n;
foreach (var prime in primes) Console.Write(prime + “, ”);
}
public static bool IsPrime(int numberToTest) // WARNING: Buggy!
{
// 2 is a weird prime: it’s even. Test for it explicitly.
if (numberToTest == 2) return true;
// Anything that’s less than 2 or that’s even is not prime
if (numberToTest < 2 || (numberToTest & 1) == 0) return false;
// Test all odd numbers less than the sqrt of the target number.
// If the target is divisible by any of them, it’s not prime.
// We don’t test evens, because if the target is divisible
// by an even, the target is also even, which we already checked for.
int upperBound = (int)Math.Sqrt(numberToTest);
for (int i = 3; i < upperBound; i += 2)
{
if ((numberToTest % i) == 0) return false;
}
// It’s prime!
return true;
}

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

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

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

Чтобы отобразить стек вызовов другой поток в окне «Стек вызовов», необходимо внести другой поток текущего двойным щелчком в окне «потоки». Метод выполняется в (используется в верхней части стека вызовов) обозначается желтая стрелка и называется «верхние рамки»,«конечный кадр»или рамки активного стека"." Это метод, из которого поток продолжит выполнение при выходе отладчик и продолжить выполнение приложения. По умолчанию кадр стека активной является текущий фрейм стека — другими словами, метод, который дисков переменной проверки, который мы будут описаны далее.


Рис. 4 Задание условной точки останова

В окнах переменных используются для проверки значений переменных в приложении. Переменные локальные методы обычно просмотреть в окнах «Локальные» и «видимые;глобальное состояние (переменные, объявленные в методе не) можно проверить, добавив их окно наблюдения. Начиная с Visual Studio 2005, разработчики больше и больше проверить состояние наведение их указатели мыши с переменной процентной и просмотрев результирующий всплывающих подсказка (который может рассматриваться как ярлык для окна "Быстрая проверка). Важно отметить, что значения переменных могут быть отображены только, если переменные находятся в области текущего кадра стека (то, как мы ранее установлено по умолчанию кадр активного стека текущего потока).

Для изучения переменных, которые были ранее области стек вызовов потока, необходимо изменить текущий фрейм стека, дважды щелкните рамку стека, нужно проверить в окне «Стек вызовов». На этом этапе новые текущий фрейм стека обозначается зеленый заключительного кривой стрелки (хотя кадр стека активного сохраняет желтая стрелка). Если вы хотите проверить переменные в другом потоке необходимо изменить текущий поток в окне «потоки» и включить текущего кадра на его стека вызовов в окне «Стек вызовов».

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

Параллельный стеки

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

Сохранить недвижимости экрана, но также для указания методов интерес к сценариям параллелизма, окна объединяет в том же узле сегменты стека вызова, которые являются общими между потоками их корне. Например на рис. 1 можно увидеть стеков вызовов потоков три в одном представлении. На рисунке показан один поток, из основного необходимо для B и два потока, запущен из одного внешнего кода и затем произошло. Один из этих потоков продолжение, B и некоторые внешнего кода и другой поток продолжена, C и некоторых AnonymousMethod. AnonymousMethod также кадр стека активной, и он принадлежит к текущему потоку. Многие другие функции поддерживаются в этом окне, такие как масштаб, bird's-eye представление фильтрацию потоков через Пометка и большая часть же функций, уже доступных в окне «Стек вызовов».


Choosing the Freeze All Threads But This Command
На рис. 5 Выбор замораживания все потоки, но эта команда


Coalescing of Stack Frames
Рисунок 6 Coalescing кадров стека

Если приложение создает задачи вместо потоков, можно переключиться на ориентированных задач представления. В этом представлении опущены стеков вызовов потоков, не выполнив задач. Кроме того обрезается стеков вызовов потоков для представления стеки вызова реальных задач--, стек вызовов один поток может включать два или три задачи, необходимо разделить и просмотреть отдельно. Специальную возможность окна параллельных стеки позволяет Поворот диаграммы отдельный метод и четко просмотрите источники вызовов и callees, метод контекста.

На рис. 7 кода на основе задач с зависимости

static void Main(string[] args) // WARNING: Buggy!
{
var task1a = Task.Factory.StartNew(Step1a);
var task1b = Task.Factory.StartNew(Step1b);
var task1c = Task.Factory.StartNew(Step1c);
Task.WaitAll(task1a, task1b, task1c);
var task2a = Task.Factory.StartNew(Step2a);
var task2b = Task.Factory.StartNew(Step2b);
var task2c = Task.Factory.StartNew(Step2c);
Task.WaitAll(task1a, task1b, task1c);
var task3a = Task.Factory.StartNew(Step3a);
var task3b = Task.Factory.StartNew(Step3b);
var task3c = Task.Factory.StartNew(Step3c);
Task.WaitAll(task3a, task3b, task3c);
}

Параллельный задачи

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


Рисунок 8 Использование параллельных задач для поиска проблем зависимости

Возможно крупнейших разработчикам значение столбец состояния. Сведения в столбце состояния позволяет позволяет различать выполнение задач и задач, ожидающих (над другой задачей или примитив синхронизации,) или задачи, которые взаимно (специализации ожидания задачи, для которых средство обнаруживает цепочку циклического ожидания). Окне параллельных задач также отображает назначенных заданий, которые являются задачи, которые не были еще выполняться, но расстоянии в некоторых очереди, ожидающих выполнения в потоке. Пример можно увидеть в рис. 2. Дополнительные сведения о параллельных стеки и параллельных задачи содержатся в разделе блога на danielmoth.com/Blog/labels/ParallelComputing.html и документации MSDN по адресу msdn.microsoft.com/dd554943(VS.100).aspx.

На рис. 9 кода взаимоблокировки

static void Main(string[] args)
{
int transfersCompleted = 0;
Watchdog.BreakIfRepeats(() => transfersCompleted, 500);
BankAccount a = new BankAccount { Balance = 1000 };
BankAccount b = new BankAccount { Balance = 1000 };
while (true)
{
Parallel.Invoke(
() => Transfer(a, b, 100),
() => Transfer(b, a, 100));
transfersCompleted += 2;
}
}
class BankAccount { public int Balance; }
static void Transfer(BankAccount one, BankAccount two, int amount)
{
lock (one) // WARNING: Buggy!
{
lock (two)
{
one.Balance -= amount;
two.Balance += amount;
}
}
}

Найти ошибки

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

Выпуск одного

Во-первых рассмотрим код, показанный на рис. 3. Цель этого кода — для вывода простых чисел от 1 до 10,000,000, для этого параллельно. (Поддержки параллелизации предоставляется Parallel LINQ; см. blogs.msdn.com/pfxteam и msdn.microsoft.com/dd460688(VS.100).aspx для получения дополнительных сведений.) Реализация IsPrime является buggy, как можно увидеть, запустив код и просмотр первой выходных данных несколько номеров:

2, 3, 5, 7, 9, 11, 13, 15, 17, 19, 23, 25, ...

Большинство из этих чисел primes, но 9, 15 и 25 отсутствуют. Если бы в однопотоковое приложение, может легко пошагово выполнять код для поиска причины неточные результаты. Однако при выполнении пошаговое выполнение одного (выберите Debug >Шаг с заходом, например) в многопоточных программах любой поток в программе подходит для пошагового выполнения. Это означает, как вы шаг, может переходы между потоками, что более затрудняет понимание потока управления и диагностические сведения об текущее расположение в программе. Чтобы помочь с этим можно воспользоваться несколько возможностей отладчика. Первым является установка условные точки останова.

Как показано на рис. 4, можно установить точку останова (в данном случае в первой строке метода IsPrime) и указать отладчику на прервание в только при определенных выполняется условие--в этом случае, когда один из неточности "primes"вычисляется.

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

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

С ненужных поток(-и) заморожен мы можем теперь одиночный шаг через наши ошибки IsPrime. По отладке numberToTest == 25, можно легко заметить, что это прошли неправильный: цикла должно включать значение upperBound в тест, тогда как это значение в настоящее время является исключить, поскольку в цикле используется меньше - чем оператор, а не менее от или равняется. Здесь, квадратный корень из 25 — 5, и 25 делится 5, но 5 не будет необходимо проверить, поэтому 25 как простых ошибочно относится к категории.

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

PLINQ выполняется IsPrime в несколько задач, и значение numberToTest для этих задач является видимым в всплывающее окно, здесь отображается задача 1 выполняет обработку numberToTest == 8431901, пока 2 обрабатывается numberToTest 8431607 ==.

Проблемы зависимостей

В коде в на рис. 7 показан экземпляр шаблона общих параллельных приложений. Компоненты этого кода отключено несколько операций (step1a step1b, step1c, которые все методы в форме «void StepXx()"), могут выполняться параллельно и затем объединяет их. Впоследствии компоненты приложения снова с кодом, требует предыдущие операции уже быть завершено из-за зависимости операцииПобочные эффекты (например, при записи данных в некоторых общих массивов).

К сожалению этот код содержит ошибки, и разработчик, написавший его видеть некоторые неточные результаты вычисляемые третий набор из задач. Неявно том, что даже если разработчик ожидает все предыдущие задачи для выполнения, что-нибудь, amiss и фактически не все предыдущие вычислений завершена их результаты. Чтобы отладить код, разработчик задает точку останова на последнем вызове метод WaitAll и использует окне параллельных задач для просмотра текущего состояния программы, которая показана на рис. 8.

Убедиться, что достаточно окно параллельных задач показывает, задача Step2c еще выполняется, даже если запланировано задачи для шага 3. Обзор второй вызов Task.WaitAll показано, почему: из-за ошибок ввода, task1a task1b и task1c выполняется ожидал вместо их аналоги task2.

Взаимоблокировок

На рисунке 9 предоставляет типичных пример ситуации взаимоблокировки результатом не обращая внимание блокировки упорядочение. Основной код постоянно является передача денег между банковскими счетами. Способ переноса предназначен для быть поточно ориентированными, поэтому он может быть вызван одновременно из нескольких потоков. Таким образом его внутренне блокирует на BankAccount объекты передаются на него путем блокировки на первом и затем блокировку на второй. К сожалению это может привести к взаимоблокировки, как работает этот код продемонстрирую. В конечном счете отладчик прерывает выполнение при обнаружении не передает, продолжить. (Критические в выполняется с помощью кода, который выдает Debugger.Break, если обнаруживает, что не передает новый выполнены через определенный промежуток времени. Этот код включается в загрузки, прилагаемый к данной статье.)


Information on Deadlocks in Parallel Tasks
На рис. 10 сведения о взаимоблокировок в параллельных заданий

 


Parallel Stacks Showing Deadlocks
На рис. 11 параллельный стеллаж отображения взаимоблокировок

 


Method View in Parallel Stacks
На рис. 12 режиме метод параллельный стеллаж

Если вы работаете в отладчике, немедленно отображается графическое представление демонстрации, взаимоблокировки, как показано на рис. 10. На рисунке также показано, наведение указателя мыши статус Ожидание взаимно предоставляет дополнительные сведения о точно какие возможности ожидается и какой поток удерживает защищенный ресурс. Глядя на поток назначения столбца, можно просмотреть 2 ожидает на ресурсе, удерживаемые задача 1, а при наведении задачи 1 будет отображаться обратное.

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

Рис. 13 Создание Convoy блокировки

static void Main(string[] args) // WARNING: Buggy!
{
object obj = new object();
Enumerable.Range(1, 10).Select(i =>
{
var t = new Thread(() =>
{
while (true)
{
DoWork();
lock (obj) DoProtectedWork();
}
}) { Name = “Demo “ + i };
t.Start();
return t;
}).ToList().ForEach(t => t.Join());
}

 

Блокировка Convoys

Convoys блокировки может произойти при нескольких потоков постоянно конкурируют за же защищенную область.(Wikipedia предоставляет хороший сводку convoys блокировки на en.wikipedia.org/wiki/Lock_convoy).Код на рис. 13 пример quintessential convoy блокировки: несколько потоков повторно выполнив некоторые объем работы за пределами защищенную область, но получение блокировки некоторых дополнительных действий внутри, защищенные области.В зависимости от соотношения между работы внутри и вне области может привести проблемам с производительностью.Такого рода неполадок видны после выполнения программы с помощью средства как профилировщик параллелизма, доступные в Visual Studio 2010, но их может также быть зафиксировано во время выполнения с помощью инструмента отладки как окно параллельных стеки.


Lock Convoys with Parallel Stacks

Рис. 14 Блокировка Convoys с помощью параллельных стеков

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

Заключение

В этой статье вы видели примеры как окон отладчика Visual Studio 2010 может упростить процесс поиска ошибок в коде на основе задач.API основе задач для управляемого и машинного кода являются более чем что нам удалось показать в примерах коротким в данной статье, и мы рекомендуем вам изучить их более 4 .NET Framework и Visual C++ 10.На передней средства в дополнение к два новых окна отладчика обсуждалось нового анализатора производительности параллелизма интегрирован в существующие профилировщик Visual Studio.

Чтобы получить ваших рук на все биты выше, загрузите бета-версии Visual Studio 2010 с msdn.microsoft.com/dd582936.aspx.

Даниэл Мотработает в параллельных вычислений платформа группы корпорации Майкрософт.С ним можно связаться через его блог по danielmoth.com/Blog.
Стивен Таубработает в параллельных вычислений платформа группы корпорации Майкрософт. Он также является пишущим редактором журнала
MSDN Magazine.