SlideShare a Scribd company logo
Многопроцессорное
Программирование
Обзорный курс (1-я лекция)
Дмитрий Цителов, 2014
cit@devexperts.com
В курсе использованы материалы из лекций Романа Елизарова и презентационных материалов Devexperts, LLC
Devexperts
Мы создаем сложные, высоконагруженные системы для финансовой сферы.
Закон Мура и “The free lunch is over”
http://www.gotw.ca/publications/concurrency-ddj.htm
Зеленая линия =
Закон Мура
Вехи в истории развития процессоров x86
• 1978 – Intel 8086 (16 битный процессор)
• 1982 – 286 (защищенный режим)
• 1985 – 386 (32 битный, страничная память)
• 1989 – 486 (FPU, конвейер дающий 1 инструкцию/такт, SMP)
• 1993 – Pentium (суперскалярный = 2 инструкции за такт)
- 1995 – Pentium Pro (внеочередное исполнение)
- 1996 – Pentium MMX (=SIMD – single instruction, multiple data; 2x32)
• 1999 – Pentium 3 (SSE = SIMD 4x32 бита float)
• 2001 – Pentium 4 (SS2 = 2x64/4x32/8x16/16x8 бита)
• 2006 – Core 2 (64 бит процессор)
Параллелизм на уровне инструкций
(ILP – instruction level parallelism)
• Способы использования ILP:
- Конвейер
- Суперскалярное исполнение
• Внеочередное исполнение
• Переименование регистров
• Спекулятивное исполнение
• Предсказание переходов
- Длинное машинное слово (VLIW –
very long instruction word)
- Векторизация (SIMD)
a = b + c; // (1)
d = e + f; // (2)
Нет зависимости по данным:
можно выполнить (1) и (2)
параллельно
Процессоры
Скалярные
(SISD)
VLIW
Супер-
скалярные
Векторные
(SIMD)
Объединение в
современных
процессорах
У параллелизма на уровне
инструкций есть предел
Параллельное
программирование
Симметричная мультипроцессорность
(SMP – symmetric multiprocessing)
CPU1 CPU2
Общая шина / X-BAR
Общая память
Два (или больше)
вычислительных ядра.
На каждом свой поток
исполняемых инструкций
Одновременная многозадачность
(SMT – simultaneous multithreading)
Поток1 Поток2
Общая память
Два (или больше) потока
одновременно исполняются
одним физическим
вычислительным ядром.
Для программиста
выглядит как SMP.
Процессор
Несимметричный доступ к памяти
(NUMA – non-uniform memory access)
CPU1 CPU2
Память1
Модель программирования
такая же что и в SMP –
общая память
Память2
Не все процессоры одинаково полезны…
• Обычный CPU
- Число ядер: единицы, на хосте - 8-16...
- Производительность: ~100 Gflops
• Intel Phi coprocessor
- Число ядер: ~60
- Производительность: ~1 Tflops
• Nvidia GeForce/Tesla GPUs
- Число CUDA ядер: ~3000
- Производительность: up to single precission ~4 Tflops
Гибридные вычисления –
бурно развивающееся
направление
Операционные системы
• Типы
- Однозадачные
- Система с пакетными заданиями (batch processing)
- Многозадачные / c разделением времени (time-sharing)
• Кооперативная многозадачность (cooperative multitasking)
• Вытесняющая многозадачность (preemptive multitasking)
• История многозадачности
- Изначально нужно было для раздела одной дорогой машины
между разными пользователями
- Теперь нужно для использования ресурсов одной многоядерной
машины для множества задач
Основные понятия в современных ОС
• Процесс – владеет памятью и ресурсами
• Поток – контекст исполнения внутри процесса
- В одном процессе может быть несколько потоков
- Все потоки работают с общей памятью процесса
- У потока есть собственная область памяти (локальные
переменные, стек вызовов, статические переменные потока
(threadlocal)
• Но в теории мы их будем смешивать
- В научных работах исторически сложилось называть потоки
исполнения «процессы» и обозначать большими буквами P, Q,
R…
Чем помогает параллельное программирование?
• Разделение задачи на отдельные потоки управления
- Декомпозиция сложного поведения
- Повышает «отзывчивость» программ
- Работа с вводом/выводом
- Балансировка распределения вычислительных ресурсов
- Обеспечение надѐжности
• Обработка больших объѐмов данных по частям
- Максимальное использование доступных вычислительных
ресурсов (сам процессор не догадается, как подключить
дополнительные ядра)
• Распределѐнные вычисления – всѐ тоже самое, но с
использованием многих компьютеров
Будет рассмотрено подробнее позже…
Модели программирования
• «Классическое» однопоточное / однозадачное
- Можем использовать ресурсы многоядерной системы только
запустив множество разных, независимых задач
• Многозадачное программирование
- Возможность использовать ресурсы многоядерной системы в
рамках решения одной задачи
- Варианты:
• Модель с общей памятью
• Модель с передачей сообщений (распределенное
программирование)
Зачем нужна формальная модель параллельных
вычислений?
• [Теория] Чтобы доказывать:
- корректность алгоритмов
- невозможность построения тех или
иных алгоритмов
- минимально-необходимые требования
для тех или иных алгоритмов
• [Практика] Для формализации
отношений между прикладным
программистом и разработчиком
компилятора и системы исполнения
кода
Модель с общими объектами (общей памятью)
Поток1
Поток2
ПотокN
Общая память
Объект M
Объект 2
Объект 1
Общие объекты
• Потоки выполняют операции над общими, разделяемыми
объектами
• В этой модели не важны операции внутри потоков:
- Вычисления
- Обновления регистров процессора
- Обновления стека потока
- Обновления любой другой локальной для потока памяти
• Важна только коммуникация между потоками
• В этой модели единственный тип коммуникации между
потоками – это работа с общими объектами
Общие переменные
• Общие переменные – это просто простейший тип общего
объекта:
- У него есть значение определенного типа
- Есть операция чтения (read) и записи (write).
• Общие переменные – это базовые строительные блоки для
параллельных алгоритмов
• Модель с общими переменными – это хорошая абстракция
современных многопроцессорных систем и многопоточных ОС
- На практике, это область памяти процесса, которая
одновременно доступна для чтения и записи всем потокам,
исполняемым в данном процессе
В теоретических трудах общие переменные называют регистрами
Модели с передачей сообщений
• Можем моделировать параллельную систему разрешив
потоком передавать друг другу сообщения
• Используется для распределенных систем
• Обе модели имею эквивалентную силу
- Можно смоделировать одну на другой и наоборот
- Но разную производительность
Параллельный
Parallel
Многопоточный
Concurrent
[shared memory]
Распределѐнный
Distributed
[message passing]
* NOTE: Нет всеобщего согласия по терминологии
Трудности перевода…
Свойства параллельных программ
Моделирование исполнений через
чередование операций
Пример
thread P:
0: x = 1
1: print x
2: stop
thread Q:
0: x = 2
1: print x
2: stop
P0,Q0
x=0
(-, -)
P1,Q0
x=1
(-, -)
P0,Q1
x=2
(-, -)
P2,Q0
x=1
(1, -)
P1,Q1
x=2
(-, -)
P1,Q1
x=1
(-, -)
P0,Q2
x=2
(-, 2)
P2,Q2
x=2
(1, 2)
P2,Q2
x=2
(2, 2)
+1 state not shown +2 states not shown
P2,Q2
x=1
(1, 1)
+2 states not shown
P2,Q2
x=1
(2, 1)
+1 state not shown
Всего 17 состояний
shared int x
Обсуждение модели
• Эта модель не «параллельна»
- Все операции происходят последовательно (только порядок
заранее не задан)
• А на самом деле на реальных процессорах операции чтения и
записи памяти не мгновенные и происходит параллельно как
в разных ядрах так и внутри одного ядра
- В реальности процессор обменивается с памятью сообщениями
чтения/записи и таких сообщений находящихся в обработке
одновременно может быть очень много (!)
Модель чередования не отражает фактическую
реальность. Можно ли еѐ использовать для анализа
алгоритмов на практике? Когда еѐ можно использовать?
Физическая реальность (1)
Физическая реальность (2)
x
t
a b
Световой конус
с
Модель «произошло до»
Модель глобального времени
P
Q
R
Будем использовать эту модель во всех иллюстрациях
Обсуждение глобального времени
• Это всего лишь механизм, позволяющий визуализировать
факт существования параллельных операций
• При доказательстве различных фактов и анализе свойств
[исполнений] системы время не используется
- Анализируются только операции и отношения «произошло до»
На самом деле, никакого глобального
времени нет и не может быть из-за
физических ограничений
Свойства исполнений над общими объектами
http://blog.lib.umn.edu/cspg/electionacademy/images/sharing.jpg
Операции над общими объектами
x.w(1)
объект
операция
аргумент
x.r:1
объект
операция
результат
Запись (write)
общей переменной
Чтение (read)
общей переменной
Последовательное исполнение
P
Q
x.w(1)
x.r:1 последовательное
P
Q
x.w(1)
x.r:1 параллельное
Правильное исполнение
P
Q
x.w(1)
x.r: 1 правильное
P
Q
x.w(1)
x.r: 1 неправильное
x.r: 1
x.r: 1
Нас интересуют только правильные исполнения
Последовательная спецификация объекта
Допустимое последовательное исполнение
• Последовательное исполнение является допустимым (legal),
если выполнены последовательные спецификации всех
объектов
P
Q
x.w(1)
x.r:1
Допустимое
P
Q
x.w(1)
x.r:0
Недопустимое
Условия согласованности (корректности)
• Как определить допустимость параллельного исполнения?
- Сопоставив ему эквивалентное (состоящее из тех же событий и
операций) допустимое последовательное исполнение
- Как именно – тут есть варианты: условия согласованности
• Аналог «корректности» в мире параллельного
программирования
• Условий согласованности много, из них основные
(удовлетворяющие базовому требованию) это:
- Последовательная согласованность
- Линеаризуемость
Базовое требование: корректные
последовательные программы должны
работать корректно в одном потоке
Последовательная согласованность (1)
• Исполнение последовательно согласовано, если можно
сопоставить эквивалентное ему допустимое
последовательное исполнение, которое сохраняет
программный порядок – порядок операций на каждом потоке
P
Q
x.w(1)
x.r:0 Последовательно
согласовано
P
Q
x.w(1)
x.r:2 НЕТ
R
x.r:1
S
x.r:0
x.r:1
Последовательная согласованность (2)
• Используется как условие корректности исполнения всей
системы в целом
- когда нет никакого «внешнего» наблюдателя, который может
«увидеть» фактический порядок между операциями на разных
процессах
• Модель памяти языка программирования и системы
исполнения кода использует последовательную
согласованность
- В том числе, С++11 и Java 5 (JMM = JLS Chapter 17)
Последовательная согласованность не говорит о том, когда
операция физически на самом деле была выполнена
Последовательная согласованность (3)
• Последовательная согласованность на каждом объекте по
отдельности не влечет последовательную согласованность
всего исполнения
P
Q
s.enc(1)
t.enq(2)
t.enc(1)
s.enq(2)
s.deq:2
t.deq:1
Последовательную согласованность нельзя использовать для
спецификации поведения отдельных объектов в системе
Линеаризуемость
• Исполнение линеаризуемо если можно сопоставить
эквивалентное ему допустимое последовательное
исполнение, которое сохраняет порядок «произошло до»
P
x.w(1)
Последовательно
согласовано,
не линеаризуемо
P
Q
x.w(1)
x.r:0 линеаризуемо
Q
x.r:0
x.r:1
Свойства линеаризуемости
Линеаризуемость в глобальном времени
• В глобальном времени исполнение линеаризуемо тогда и
только тогда, когда точки линеаризации могут быть выбраны
так, что
P
Q
x.w(1)
x.r:1
R
x.r:0
линеаризуемо
Исполнение системы, выполняющей операции
над линеаризуемыми (атомарными) объектами,
можно анализировать в модели чередования
Линеаризуемость и чередование
Иерархия линеаризуемых объектов
• Из более простых линеаризуемых объектов можно делать
линеаризуемые объекты более высокого уровня
- Исполнения над объектами низкого уровня линеаризуемо, значит
для каждого из них можно назначить время
- Значит исполнение более высокого уровня можно анализировать
в глобальном времени
- Точка линеаризации в исполнении более высокого уровня это
выполнение какой-то операции более низкого уровня. Ведь
операции более низкого уровня атомарные (им можно назначить
время).
- Доказав линеаризуемость (через нахождения точек
линеаризации), можем абстрагироваться от деталей реализации
и строить объекты еще более высокого уровня
Иерархия линеаризуемых объектов
• Из более простых линеаризуемых объектов можно делать
линеаризуемые объекты более высокого уровня
• Когда говорят что какой-то объект безопасен для
использования из нескольких потоков (thread-safe), то по
умолчанию имеют в виду линеаризуемость операций над ним
Доказав линеаризуемость сложного объекта, можно
абстрагироваться от деталей
реализации в нем, считать операции над ним
атомарными и строить объекты более высокого уровня
Построение линеаризуемых объектов
• Как построить линеаризуемый объект из последовательного?
- Если у нас есть атомарные регистры как аппаратные примитивы
class Queue:
int head
int tail
E elems[N]
def enq(x):
elems[tail] = x
tail = (tail + 1) % N
def deq:
result = elems[head]
head = (head + 1) % N
return result
Не безопасно использовать из
разных потоков (not thread-safe)
http://www.flickr.com/photos/xserve/368758286/
Взаимное исключение
• Защитим каждую операцию специальным объектом mutex
(mutual exclusion), также известного как блокировка (lock)
class Queue:
Mutex mutex
…
def enq(x):
mutex.lock
…
mutex.unlock
def deq:
mutex.lock
…
mutex.unlock
return result
Так чтобы реализация (тело) каждого
метода не выполнялось
одновременно, то есть все тела
методов выполнялись бы
последовательно.
Значит будет работать обычный
последовательный код
Взаимное исключение формально
• Главное свойство называется
взаимное исключение.
• X1: Критические секции не могут
выполняться параллельно:
• Это значит, что выполнение
критических секций будет
линеаризуемо
• Это требование корректности
протокола взаимной блокировки
thread Pid:
loop forever:
1: nonCriticalSection
2: mutex.lock
3: criticalSection
4: mutex.unlock
Протокол
ijji CSCSCSCSjiji :,
Взаимное исключение, попытка 1
• Этот протокол не гарантирует
взаимное исключение
- Оба потока могут оказаться в
критической секции
одновременно
shared boolean want
def lock:
1: while want:
2: pass // wait
3: want = true
def unlock:
4: want = false
Взаимное исключение, попытка 2
• Этот протокол гарантирует
взаимное исключение
• Но не нет никакой гарантии
прогресса.
• X2: Отсутствие взаимной
блокировки (deadlock-freedom).
Если несколько потоков
пытаются войти в критическую
секцию, то хотя бы один из них
должен войти в критическую
секцию за конечное время. *
threadlocal int id // 0 or 1
shared boolean want[2]
def lock:
1: want[id] = true
2: while want[1 - id]:
3: pass
def unlock:
4: want[id] = false
* При условии что критические секции выполняются за конечное время
Взаимное исключение, попытка 3
• Есть взаимное исключение и
прогресс
• Но можем заходить только по
очереди. Поток будет голодать.
• X3: Потребуем отсутствие
голодания (starvation-freedom).
Если какой-то поток пытается
войти в критическую секцию, то
он войдет в критическую секцию
за конечное время. *
threadlocal int id // 0 or 1
shared int victim
def lock:
1: victim = id
2: while victim == id:
3: pass
def unlock:
4: pass
* При условии что критические секции выполняются за конечное время
Взаимное исключение, алгоритм Петерсона
• Гарантирует взаимное
исключение, отсутствие
взаимной блокировки и
отсутствие голодания.
• Порядок операций присваивания
(1 и 2) в этом коде важен
• Не первый изобретенный (1981),
но простейший алгоритм для
двух потоков.
threadlocal int id // 0 or 1
shared boolean want[2]
shared int victim
def lock:
1: want[id] = true
2: victim = id
3: while want[1-id] and
4: victim == id:
5: pass
def unlock:
6: want[id] = false
Взаимное исключение, алгоритм Петерсона
для N потоков
• Гарантирует взаимное
исключение, отсутствие
блокировки и отсутствие
голодания.
• Но не очень честный.
- Невезучий поток может ждать
пока другие потоки O(N2) раз
войдут в критическую секцию
(квадратичное ожидание)
- Более честно было бы
линейное ожидание
threadlocal int id // 0 to N-1
shared int level[N]
shared int victim[N]
def lock:
1: for j = 1..N-1:
2: level[id] = j
3: victim[j] = id
4: while exist k: k != id and
5: level[k] >= j and
6: victim[j] == id:
7: pass
def unlock:
8: level[id] = 0
Взаимное исключение, алгоритм Лампорта
(алгоритм булочника - вариант 1)
• Есть корректность и все
свойства прогресса
• Обладает свойством
первый пришел, первый
обслужен (first-come, first-
served – FCFS)
- Это сильнее чем линейное
ожидание
• Но метки должны быть
бесконечными (их можно
заменить на конечные
метки)
threadlocal int id // 0 to N-1
shared boolean want[N] init false
shared int label[N] init 0
def lock:
want[id] = true
label[id] = max(label) + 1
while exists k: k != id and
want[k] and
(label[k], k) < (label[id], id) :
pass
def unlock:
want[id] = false
doorway
Взаимное исключение, алгоритм Лампорта
(алгоритм булочника - вариант 2)
• Те же свойства
- И метки тоже могут быть
бесконечными, хотя мы их и
сбрасываем при выходе из
критической секции
threadlocal int id // 0 to N-1
shared boolean сhoosing[N] init false
shared int label[N] init inf
def lock:
choosing[id] = true
label[id] = max(label) + 1
choosing[id] = false
while exists k: k != id and
(choosing[k] or
(label[k], k) < (label[id], id)) :
pass
def unlock:
label[id] = inf
doorway
Использование взаимного исключения
(блокировки)
• Любой последовательный объект можно сделать
параллельным, линеаризуемым.
- Это т.н. грубая блокировка. Блокируем всю операцию целиком.
- Можно использовать тонкую блокировку.
• Блокировать только операции над общими объектами внутри
метода, но не вызов метода целиком.
• Но тогда, для обеспечения линеаризуемости, нужно
использовать двухфазную блокировку.
Проблемы взаимного исключения:
взаимная блокировка (deadlock)
thread P:
1: mutex1.lock
2: mutex2.lock
….
3: mutex2.unlock
4: mutex1.unlock
thread Q:
1: mutex2.lock
2: mutex1.lock
….
3: mutex1.unlock
4: mutex2.unlock
P Q
Цикл в графе ожидания приводит к
взаимной блокировке
Избежать появления такого цикла
можно с помощью иерархической
блокировки (глобально упорядочив все
блокировки)
Проблемы взаимного исключения
• Условные условия прогресса
- Отсутствие взаимной блокировки и отсутствие голодания
гарантируют прогресс только если критические секции
выполняются за конечное время
• Инверсия приоритетов
- Возникает при блокировке потоков с разным приоритетом на
одном объекте
• Последовательное выполнение операций
- Нет параллелизма при выполнении критической секции, а значит
ограничена вертикальная масштабируемость.
Закон Амдала для параллельной работы
• Максимальное убыстрение при запуске кода в N потоков если
доля S выполняется последовательно
• Даже при 5% последовательного кода (S=0.05), максимальное
убыстрение равно 20.
N
S
S
speedup
1
1
S
speedup
N
1
lim
Даже хороший Lock всѐ равно «плохой»*
*Когда нужно реально использовать все доступные ресурсы
Конфликт блокировок и переключение контекста
1. Enter Lock
2. Context Switch
3. Try to lock
4. Context Switch
5. Exit Lock
6. Context switch
and enter lock
Пример: очередь с блокировкой и lock-free
Очередь с блокировками: 250
миллионов «попугаев» за 30с.
Lock-free очередь – 750
миллионов тех же
«попугаев» за 30с.
Улучшенная очередь с
блокировками: 350 миллионов
«попугаев» за 30с.
Multiprocessor Programming Intro (lecture 1)

More Related Content

What's hot (20)

PDF
Семинар 12. Параллельное программирование на MPI (часть 5)
Mikhail Kurnosov
 
PPTX
CUDA Best Practices (2-3)
Alexander Mezhov
 
PPTX
Introduction in CUDA (1-3)
Alexander Mezhov
 
PPSX
лекция 2
Надежда Бровко
 
PDF
Архитектура и программирование потоковых многоядерных процессоров для научных...
a15464321646213
 
PPT
Системы контроля версий
Unguryan Vitaliy
 
PPTX
Параллельное программирование на современных видеокартах
Alex Tutubalin
 
PDF
Лекция 3. Оптимизация доступа к памяти (Memory access optimization, cache opt...
Mikhail Kurnosov
 
PDF
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Mikhail Kurnosov
 
PDF
Семинар 11. Параллельное программирование на MPI (часть 4)
Mikhail Kurnosov
 
PDF
Лекция 4. Векторизация кода (Code vectorization: SSE, AVX)
Mikhail Kurnosov
 
PDF
Lecture1: Introduction to Parallel Computing
Andrii Rodionov
 
PDF
Семинар 9. Параллельное программирование на MPI (часть 2)
Mikhail Kurnosov
 
PDF
Лекция 5. Основы параллельного программирования (Speedup, Amdahl's law, paral...
Mikhail Kurnosov
 
PDF
Введение в архитектуры нейронных сетей / HighLoad++ 2016
Grigory Sapunov
 
PPT
2020 03-31-lection
Olga Leshchenko
 
PPT
Number 11
vasyaua2011
 
PPTX
Денис Колодин: Low-latency и soft-realtime на Python
it-people
 
PDF
О.В.Сухорослов "Многопотчное программирование. Часть 2"
Yandex
 
PDF
Ю.В.Андреев, А.С.Дмитриев, Д.А.Куминов "Хаотические процессоры"
Anamezon
 
Семинар 12. Параллельное программирование на MPI (часть 5)
Mikhail Kurnosov
 
CUDA Best Practices (2-3)
Alexander Mezhov
 
Introduction in CUDA (1-3)
Alexander Mezhov
 
Архитектура и программирование потоковых многоядерных процессоров для научных...
a15464321646213
 
Системы контроля версий
Unguryan Vitaliy
 
Параллельное программирование на современных видеокартах
Alex Tutubalin
 
Лекция 3. Оптимизация доступа к памяти (Memory access optimization, cache opt...
Mikhail Kurnosov
 
Семинар 1. Многопоточное программирование на OpenMP (часть 1)
Mikhail Kurnosov
 
Семинар 11. Параллельное программирование на MPI (часть 4)
Mikhail Kurnosov
 
Лекция 4. Векторизация кода (Code vectorization: SSE, AVX)
Mikhail Kurnosov
 
Lecture1: Introduction to Parallel Computing
Andrii Rodionov
 
Семинар 9. Параллельное программирование на MPI (часть 2)
Mikhail Kurnosov
 
Лекция 5. Основы параллельного программирования (Speedup, Amdahl's law, paral...
Mikhail Kurnosov
 
Введение в архитектуры нейронных сетей / HighLoad++ 2016
Grigory Sapunov
 
2020 03-31-lection
Olga Leshchenko
 
Number 11
vasyaua2011
 
Денис Колодин: Low-latency и soft-realtime на Python
it-people
 
О.В.Сухорослов "Многопотчное программирование. Часть 2"
Yandex
 
Ю.В.Андреев, А.С.Дмитриев, Д.А.Куминов "Хаотические процессоры"
Anamezon
 

Similar to Multiprocessor Programming Intro (lecture 1) (20)

PDF
Distributed Systems Presentation for Business informatics students (Staroletov)
Sergey Staroletov
 
PDF
Многопроцессорным компьютерам - параллельные программы!
Tatyanazaxarova
 
PDF
Введение в проблематику разработки параллельных программ
Tatyanazaxarova
 
PDF
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
Alexey Paznikov
 
PPTX
20090720 hpc exercise1
Michael Karpov
 
PDF
О.В.Сухорослов "Параллельное программирование"
Yandex
 
PPTX
презентация 1
Nikita Zablotskiy
 
PPTX
Параллельные вычисления
Artem K
 
PPSX
Параллельные вычисления
Artem K
 
PDF
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...
Mikhail Kurnosov
 
PDF
20140413 parallel programming_kalishenko_lecture09
Computer Science Club
 
PDF
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
Alexey Paznikov
 
PPT
Лекция №9 Организация ЭВМ и систем
pianist2317
 
PPTX
Основные принципы управления процессором и процессами
kurbanovafaina
 
PDF
Процессы и потоки
Evgeniy Mironov
 
PPT
кластеры и суперкомпьютеры
nastena07051995
 
PDF
20140216 parallel programming_kalishenko_lecture01
Computer Science Club
 
PPT
инструменты параллельного программирования
Alexander Petrov
 
PDF
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Mikhail Kurnosov
 
PDF
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Mikhail Kurnosov
 
Distributed Systems Presentation for Business informatics students (Staroletov)
Sergey Staroletov
 
Многопроцессорным компьютерам - параллельные программы!
Tatyanazaxarova
 
Введение в проблематику разработки параллельных программ
Tatyanazaxarova
 
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
Alexey Paznikov
 
20090720 hpc exercise1
Michael Karpov
 
О.В.Сухорослов "Параллельное программирование"
Yandex
 
презентация 1
Nikita Zablotskiy
 
Параллельные вычисления
Artem K
 
Параллельные вычисления
Artem K
 
Лекция 6: Многопоточное программирование: часть 2 (Speedup, Amdahl's law, POS...
Mikhail Kurnosov
 
20140413 parallel programming_kalishenko_lecture09
Computer Science Club
 
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
Alexey Paznikov
 
Лекция №9 Организация ЭВМ и систем
pianist2317
 
Основные принципы управления процессором и процессами
kurbanovafaina
 
Процессы и потоки
Evgeniy Mironov
 
кластеры и суперкомпьютеры
nastena07051995
 
20140216 parallel programming_kalishenko_lecture01
Computer Science Club
 
инструменты параллельного программирования
Alexander Petrov
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Mikhail Kurnosov
 
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Mikhail Kurnosov
 
Ad

Multiprocessor Programming Intro (lecture 1)

  • 1. Многопроцессорное Программирование Обзорный курс (1-я лекция) Дмитрий Цителов, 2014 [email protected] В курсе использованы материалы из лекций Романа Елизарова и презентационных материалов Devexperts, LLC
  • 2. Devexperts Мы создаем сложные, высоконагруженные системы для финансовой сферы.
  • 3. Закон Мура и “The free lunch is over” http://www.gotw.ca/publications/concurrency-ddj.htm Зеленая линия = Закон Мура
  • 4. Вехи в истории развития процессоров x86 • 1978 – Intel 8086 (16 битный процессор) • 1982 – 286 (защищенный режим) • 1985 – 386 (32 битный, страничная память) • 1989 – 486 (FPU, конвейер дающий 1 инструкцию/такт, SMP) • 1993 – Pentium (суперскалярный = 2 инструкции за такт) - 1995 – Pentium Pro (внеочередное исполнение) - 1996 – Pentium MMX (=SIMD – single instruction, multiple data; 2x32) • 1999 – Pentium 3 (SSE = SIMD 4x32 бита float) • 2001 – Pentium 4 (SS2 = 2x64/4x32/8x16/16x8 бита) • 2006 – Core 2 (64 бит процессор)
  • 5. Параллелизм на уровне инструкций (ILP – instruction level parallelism) • Способы использования ILP: - Конвейер - Суперскалярное исполнение • Внеочередное исполнение • Переименование регистров • Спекулятивное исполнение • Предсказание переходов - Длинное машинное слово (VLIW – very long instruction word) - Векторизация (SIMD) a = b + c; // (1) d = e + f; // (2) Нет зависимости по данным: можно выполнить (1) и (2) параллельно
  • 7. У параллелизма на уровне инструкций есть предел Параллельное программирование
  • 8. Симметричная мультипроцессорность (SMP – symmetric multiprocessing) CPU1 CPU2 Общая шина / X-BAR Общая память Два (или больше) вычислительных ядра. На каждом свой поток исполняемых инструкций
  • 9. Одновременная многозадачность (SMT – simultaneous multithreading) Поток1 Поток2 Общая память Два (или больше) потока одновременно исполняются одним физическим вычислительным ядром. Для программиста выглядит как SMP. Процессор
  • 10. Несимметричный доступ к памяти (NUMA – non-uniform memory access) CPU1 CPU2 Память1 Модель программирования такая же что и в SMP – общая память Память2
  • 11. Не все процессоры одинаково полезны… • Обычный CPU - Число ядер: единицы, на хосте - 8-16... - Производительность: ~100 Gflops • Intel Phi coprocessor - Число ядер: ~60 - Производительность: ~1 Tflops • Nvidia GeForce/Tesla GPUs - Число CUDA ядер: ~3000 - Производительность: up to single precission ~4 Tflops Гибридные вычисления – бурно развивающееся направление
  • 12. Операционные системы • Типы - Однозадачные - Система с пакетными заданиями (batch processing) - Многозадачные / c разделением времени (time-sharing) • Кооперативная многозадачность (cooperative multitasking) • Вытесняющая многозадачность (preemptive multitasking) • История многозадачности - Изначально нужно было для раздела одной дорогой машины между разными пользователями - Теперь нужно для использования ресурсов одной многоядерной машины для множества задач
  • 13. Основные понятия в современных ОС • Процесс – владеет памятью и ресурсами • Поток – контекст исполнения внутри процесса - В одном процессе может быть несколько потоков - Все потоки работают с общей памятью процесса - У потока есть собственная область памяти (локальные переменные, стек вызовов, статические переменные потока (threadlocal) • Но в теории мы их будем смешивать - В научных работах исторически сложилось называть потоки исполнения «процессы» и обозначать большими буквами P, Q, R…
  • 14. Чем помогает параллельное программирование? • Разделение задачи на отдельные потоки управления - Декомпозиция сложного поведения - Повышает «отзывчивость» программ - Работа с вводом/выводом - Балансировка распределения вычислительных ресурсов - Обеспечение надѐжности • Обработка больших объѐмов данных по частям - Максимальное использование доступных вычислительных ресурсов (сам процессор не догадается, как подключить дополнительные ядра) • Распределѐнные вычисления – всѐ тоже самое, но с использованием многих компьютеров Будет рассмотрено подробнее позже…
  • 15. Модели программирования • «Классическое» однопоточное / однозадачное - Можем использовать ресурсы многоядерной системы только запустив множество разных, независимых задач • Многозадачное программирование - Возможность использовать ресурсы многоядерной системы в рамках решения одной задачи - Варианты: • Модель с общей памятью • Модель с передачей сообщений (распределенное программирование)
  • 16. Зачем нужна формальная модель параллельных вычислений? • [Теория] Чтобы доказывать: - корректность алгоритмов - невозможность построения тех или иных алгоритмов - минимально-необходимые требования для тех или иных алгоритмов • [Практика] Для формализации отношений между прикладным программистом и разработчиком компилятора и системы исполнения кода
  • 17. Модель с общими объектами (общей памятью) Поток1 Поток2 ПотокN Общая память Объект M Объект 2 Объект 1
  • 18. Общие объекты • Потоки выполняют операции над общими, разделяемыми объектами • В этой модели не важны операции внутри потоков: - Вычисления - Обновления регистров процессора - Обновления стека потока - Обновления любой другой локальной для потока памяти • Важна только коммуникация между потоками • В этой модели единственный тип коммуникации между потоками – это работа с общими объектами
  • 19. Общие переменные • Общие переменные – это просто простейший тип общего объекта: - У него есть значение определенного типа - Есть операция чтения (read) и записи (write). • Общие переменные – это базовые строительные блоки для параллельных алгоритмов • Модель с общими переменными – это хорошая абстракция современных многопроцессорных систем и многопоточных ОС - На практике, это область памяти процесса, которая одновременно доступна для чтения и записи всем потокам, исполняемым в данном процессе В теоретических трудах общие переменные называют регистрами
  • 20. Модели с передачей сообщений • Можем моделировать параллельную систему разрешив потоком передавать друг другу сообщения • Используется для распределенных систем • Обе модели имею эквивалентную силу - Можно смоделировать одну на другой и наоборот - Но разную производительность
  • 21. Параллельный Parallel Многопоточный Concurrent [shared memory] Распределѐнный Distributed [message passing] * NOTE: Нет всеобщего согласия по терминологии Трудности перевода…
  • 24. Пример thread P: 0: x = 1 1: print x 2: stop thread Q: 0: x = 2 1: print x 2: stop P0,Q0 x=0 (-, -) P1,Q0 x=1 (-, -) P0,Q1 x=2 (-, -) P2,Q0 x=1 (1, -) P1,Q1 x=2 (-, -) P1,Q1 x=1 (-, -) P0,Q2 x=2 (-, 2) P2,Q2 x=2 (1, 2) P2,Q2 x=2 (2, 2) +1 state not shown +2 states not shown P2,Q2 x=1 (1, 1) +2 states not shown P2,Q2 x=1 (2, 1) +1 state not shown Всего 17 состояний shared int x
  • 25. Обсуждение модели • Эта модель не «параллельна» - Все операции происходят последовательно (только порядок заранее не задан) • А на самом деле на реальных процессорах операции чтения и записи памяти не мгновенные и происходит параллельно как в разных ядрах так и внутри одного ядра - В реальности процессор обменивается с памятью сообщениями чтения/записи и таких сообщений находящихся в обработке одновременно может быть очень много (!) Модель чередования не отражает фактическую реальность. Можно ли еѐ использовать для анализа алгоритмов на практике? Когда еѐ можно использовать?
  • 27. Физическая реальность (2) x t a b Световой конус с
  • 29. Модель глобального времени P Q R Будем использовать эту модель во всех иллюстрациях
  • 30. Обсуждение глобального времени • Это всего лишь механизм, позволяющий визуализировать факт существования параллельных операций • При доказательстве различных фактов и анализе свойств [исполнений] системы время не используется - Анализируются только операции и отношения «произошло до» На самом деле, никакого глобального времени нет и не может быть из-за физических ограничений
  • 31. Свойства исполнений над общими объектами http://blog.lib.umn.edu/cspg/electionacademy/images/sharing.jpg
  • 32. Операции над общими объектами x.w(1) объект операция аргумент x.r:1 объект операция результат Запись (write) общей переменной Чтение (read) общей переменной
  • 34. Правильное исполнение P Q x.w(1) x.r: 1 правильное P Q x.w(1) x.r: 1 неправильное x.r: 1 x.r: 1 Нас интересуют только правильные исполнения
  • 36. Допустимое последовательное исполнение • Последовательное исполнение является допустимым (legal), если выполнены последовательные спецификации всех объектов P Q x.w(1) x.r:1 Допустимое P Q x.w(1) x.r:0 Недопустимое
  • 37. Условия согласованности (корректности) • Как определить допустимость параллельного исполнения? - Сопоставив ему эквивалентное (состоящее из тех же событий и операций) допустимое последовательное исполнение - Как именно – тут есть варианты: условия согласованности • Аналог «корректности» в мире параллельного программирования • Условий согласованности много, из них основные (удовлетворяющие базовому требованию) это: - Последовательная согласованность - Линеаризуемость Базовое требование: корректные последовательные программы должны работать корректно в одном потоке
  • 38. Последовательная согласованность (1) • Исполнение последовательно согласовано, если можно сопоставить эквивалентное ему допустимое последовательное исполнение, которое сохраняет программный порядок – порядок операций на каждом потоке P Q x.w(1) x.r:0 Последовательно согласовано P Q x.w(1) x.r:2 НЕТ R x.r:1 S x.r:0 x.r:1
  • 39. Последовательная согласованность (2) • Используется как условие корректности исполнения всей системы в целом - когда нет никакого «внешнего» наблюдателя, который может «увидеть» фактический порядок между операциями на разных процессах • Модель памяти языка программирования и системы исполнения кода использует последовательную согласованность - В том числе, С++11 и Java 5 (JMM = JLS Chapter 17) Последовательная согласованность не говорит о том, когда операция физически на самом деле была выполнена
  • 40. Последовательная согласованность (3) • Последовательная согласованность на каждом объекте по отдельности не влечет последовательную согласованность всего исполнения P Q s.enc(1) t.enq(2) t.enc(1) s.enq(2) s.deq:2 t.deq:1 Последовательную согласованность нельзя использовать для спецификации поведения отдельных объектов в системе
  • 41. Линеаризуемость • Исполнение линеаризуемо если можно сопоставить эквивалентное ему допустимое последовательное исполнение, которое сохраняет порядок «произошло до» P x.w(1) Последовательно согласовано, не линеаризуемо P Q x.w(1) x.r:0 линеаризуемо Q x.r:0 x.r:1
  • 43. Линеаризуемость в глобальном времени • В глобальном времени исполнение линеаризуемо тогда и только тогда, когда точки линеаризации могут быть выбраны так, что P Q x.w(1) x.r:1 R x.r:0 линеаризуемо
  • 44. Исполнение системы, выполняющей операции над линеаризуемыми (атомарными) объектами, можно анализировать в модели чередования Линеаризуемость и чередование
  • 45. Иерархия линеаризуемых объектов • Из более простых линеаризуемых объектов можно делать линеаризуемые объекты более высокого уровня - Исполнения над объектами низкого уровня линеаризуемо, значит для каждого из них можно назначить время - Значит исполнение более высокого уровня можно анализировать в глобальном времени - Точка линеаризации в исполнении более высокого уровня это выполнение какой-то операции более низкого уровня. Ведь операции более низкого уровня атомарные (им можно назначить время). - Доказав линеаризуемость (через нахождения точек линеаризации), можем абстрагироваться от деталей реализации и строить объекты еще более высокого уровня
  • 46. Иерархия линеаризуемых объектов • Из более простых линеаризуемых объектов можно делать линеаризуемые объекты более высокого уровня • Когда говорят что какой-то объект безопасен для использования из нескольких потоков (thread-safe), то по умолчанию имеют в виду линеаризуемость операций над ним Доказав линеаризуемость сложного объекта, можно абстрагироваться от деталей реализации в нем, считать операции над ним атомарными и строить объекты более высокого уровня
  • 47. Построение линеаризуемых объектов • Как построить линеаризуемый объект из последовательного? - Если у нас есть атомарные регистры как аппаратные примитивы class Queue: int head int tail E elems[N] def enq(x): elems[tail] = x tail = (tail + 1) % N def deq: result = elems[head] head = (head + 1) % N return result Не безопасно использовать из разных потоков (not thread-safe)
  • 49. Взаимное исключение • Защитим каждую операцию специальным объектом mutex (mutual exclusion), также известного как блокировка (lock) class Queue: Mutex mutex … def enq(x): mutex.lock … mutex.unlock def deq: mutex.lock … mutex.unlock return result Так чтобы реализация (тело) каждого метода не выполнялось одновременно, то есть все тела методов выполнялись бы последовательно. Значит будет работать обычный последовательный код
  • 50. Взаимное исключение формально • Главное свойство называется взаимное исключение. • X1: Критические секции не могут выполняться параллельно: • Это значит, что выполнение критических секций будет линеаризуемо • Это требование корректности протокола взаимной блокировки thread Pid: loop forever: 1: nonCriticalSection 2: mutex.lock 3: criticalSection 4: mutex.unlock Протокол ijji CSCSCSCSjiji :,
  • 51. Взаимное исключение, попытка 1 • Этот протокол не гарантирует взаимное исключение - Оба потока могут оказаться в критической секции одновременно shared boolean want def lock: 1: while want: 2: pass // wait 3: want = true def unlock: 4: want = false
  • 52. Взаимное исключение, попытка 2 • Этот протокол гарантирует взаимное исключение • Но не нет никакой гарантии прогресса. • X2: Отсутствие взаимной блокировки (deadlock-freedom). Если несколько потоков пытаются войти в критическую секцию, то хотя бы один из них должен войти в критическую секцию за конечное время. * threadlocal int id // 0 or 1 shared boolean want[2] def lock: 1: want[id] = true 2: while want[1 - id]: 3: pass def unlock: 4: want[id] = false * При условии что критические секции выполняются за конечное время
  • 53. Взаимное исключение, попытка 3 • Есть взаимное исключение и прогресс • Но можем заходить только по очереди. Поток будет голодать. • X3: Потребуем отсутствие голодания (starvation-freedom). Если какой-то поток пытается войти в критическую секцию, то он войдет в критическую секцию за конечное время. * threadlocal int id // 0 or 1 shared int victim def lock: 1: victim = id 2: while victim == id: 3: pass def unlock: 4: pass * При условии что критические секции выполняются за конечное время
  • 54. Взаимное исключение, алгоритм Петерсона • Гарантирует взаимное исключение, отсутствие взаимной блокировки и отсутствие голодания. • Порядок операций присваивания (1 и 2) в этом коде важен • Не первый изобретенный (1981), но простейший алгоритм для двух потоков. threadlocal int id // 0 or 1 shared boolean want[2] shared int victim def lock: 1: want[id] = true 2: victim = id 3: while want[1-id] and 4: victim == id: 5: pass def unlock: 6: want[id] = false
  • 55. Взаимное исключение, алгоритм Петерсона для N потоков • Гарантирует взаимное исключение, отсутствие блокировки и отсутствие голодания. • Но не очень честный. - Невезучий поток может ждать пока другие потоки O(N2) раз войдут в критическую секцию (квадратичное ожидание) - Более честно было бы линейное ожидание threadlocal int id // 0 to N-1 shared int level[N] shared int victim[N] def lock: 1: for j = 1..N-1: 2: level[id] = j 3: victim[j] = id 4: while exist k: k != id and 5: level[k] >= j and 6: victim[j] == id: 7: pass def unlock: 8: level[id] = 0
  • 56. Взаимное исключение, алгоритм Лампорта (алгоритм булочника - вариант 1) • Есть корректность и все свойства прогресса • Обладает свойством первый пришел, первый обслужен (first-come, first- served – FCFS) - Это сильнее чем линейное ожидание • Но метки должны быть бесконечными (их можно заменить на конечные метки) threadlocal int id // 0 to N-1 shared boolean want[N] init false shared int label[N] init 0 def lock: want[id] = true label[id] = max(label) + 1 while exists k: k != id and want[k] and (label[k], k) < (label[id], id) : pass def unlock: want[id] = false doorway
  • 57. Взаимное исключение, алгоритм Лампорта (алгоритм булочника - вариант 2) • Те же свойства - И метки тоже могут быть бесконечными, хотя мы их и сбрасываем при выходе из критической секции threadlocal int id // 0 to N-1 shared boolean сhoosing[N] init false shared int label[N] init inf def lock: choosing[id] = true label[id] = max(label) + 1 choosing[id] = false while exists k: k != id and (choosing[k] or (label[k], k) < (label[id], id)) : pass def unlock: label[id] = inf doorway
  • 58. Использование взаимного исключения (блокировки) • Любой последовательный объект можно сделать параллельным, линеаризуемым. - Это т.н. грубая блокировка. Блокируем всю операцию целиком. - Можно использовать тонкую блокировку. • Блокировать только операции над общими объектами внутри метода, но не вызов метода целиком. • Но тогда, для обеспечения линеаризуемости, нужно использовать двухфазную блокировку.
  • 59. Проблемы взаимного исключения: взаимная блокировка (deadlock) thread P: 1: mutex1.lock 2: mutex2.lock …. 3: mutex2.unlock 4: mutex1.unlock thread Q: 1: mutex2.lock 2: mutex1.lock …. 3: mutex1.unlock 4: mutex2.unlock P Q Цикл в графе ожидания приводит к взаимной блокировке Избежать появления такого цикла можно с помощью иерархической блокировки (глобально упорядочив все блокировки)
  • 60. Проблемы взаимного исключения • Условные условия прогресса - Отсутствие взаимной блокировки и отсутствие голодания гарантируют прогресс только если критические секции выполняются за конечное время • Инверсия приоритетов - Возникает при блокировке потоков с разным приоритетом на одном объекте • Последовательное выполнение операций - Нет параллелизма при выполнении критической секции, а значит ограничена вертикальная масштабируемость.
  • 61. Закон Амдала для параллельной работы • Максимальное убыстрение при запуске кода в N потоков если доля S выполняется последовательно • Даже при 5% последовательного кода (S=0.05), максимальное убыстрение равно 20. N S S speedup 1 1 S speedup N 1 lim
  • 62. Даже хороший Lock всѐ равно «плохой»* *Когда нужно реально использовать все доступные ресурсы
  • 63. Конфликт блокировок и переключение контекста 1. Enter Lock 2. Context Switch 3. Try to lock 4. Context Switch 5. Exit Lock 6. Context switch and enter lock
  • 64. Пример: очередь с блокировкой и lock-free Очередь с блокировками: 250 миллионов «попугаев» за 30с. Lock-free очередь – 750 миллионов тех же «попугаев» за 30с. Улучшенная очередь с блокировками: 350 миллионов «попугаев» за 30с.

Editor's Notes

  • #4: WP: Зако́нМу́ра — эмпирическое наблюдение, изначально сделанное Гордоном Муром, согласно которому (в современной формулировке) количество транзисторов, размещаемых на кристалле интегральной схемы, удваивается каждые 24 месяца. Часто цитируемый интервал в 18 месяцев связан с прогнозом Давида Хауса из Intel, по мнению которого производительность процессоров должна удваиваться каждые 18 месяцев из-за сочетания роста количества транзисторов и быстродействия каждого из них.
  • #65: L1 cache reference 0.5 nsBranch mispredict 5 nsL2 cache reference 7 nsMutex lock/unlock 100 nsMain memory reference 100 nsContext switch ~ 1500-4000 ns