Мультипроцессорность на десктопе

В этой статье мы узнаем, как добиться многопроцессорности с помощью Python и обсудим его передовые концепции.

Что такое многопроцессорность в Python?

Многопроцессорность в Python – это способность системы запускать несколько процессов параллельно. Проще говоря, многопроцессорность использует два или более ЦП в одной компьютерной системе. Этот метод также может распределять задачи между процессами.

Блоки обработки совместно используют основную память и периферийные устройства для одновременной обработки программ. Многопроцессорное приложение разбивается на более мелкие части и работает независимо. Каждый процесс назначается процессору операционной системой.

Python предоставляет встроенный пакет, называемый multiprocessing, который поддерживает процессы подкачки. Прежде чем работать с многопроцессорной обработкой, мы должны ознакомиться с объектом процесса.

Зачем нужна многопроцессорность?

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

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

Следовательно, многопроцессорность необходима для выполнения нескольких задач одновременно без прерывания. Это также позволяет легко отслеживать все задачи. Вот почему важна необходимость концепции многопроцессорности:

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

В многопроцессорном режиме ЦП может назначать несколько задач, каждая из которых имеет свой собственный процессор.

Модуль многопроцессорности в Python

Python предоставляет модуль многопроцессорности для выполнения нескольких задач в одной системе. Он предлагает удобный и интуитивно понятный API для работы с многопроцессорной обработкой.

Давайте разберемся на простом примере множественной обработки.

Пример –

from multiprocessing import Process
def disp():
print ('Hello !! Welcome to Python Tutorial')
if __name__ == '__main__':
p = Process(target=disp)
p.start()
p.join()

Выход:

'Hello !! Welcome to Python Tutorial'

Объяснение:

В приведенном выше коде мы импортировали класс Process, а затем создали объект Process в функции disp(). Затем мы запустили процесс с помощью метода start() и завершили процесс с помощью метода join(). Мы также можем передавать аргументы в объявленную функцию, используя ключевые слова args.

Давайте разберемся в следующем примере многопроцессорной обработки с аргументами.

Пример – 2

# Python multiprocessing example
# importing the multiprocessing module

import multiprocessing
def cube(n):
# This function will print the cube of the given number
print(«The Cube is: {}».format(n * n * n))

def square(n):
# This function will print the square of the given number
print(«The Square is: {}».format(n * n))

if __name__ == «__main__»:
# creating two processes
process1 = multiprocessing.Process(target= square, args=(5, ))
process2 = multiprocessing.Process(target= cube, args=(5, ))

# Here we start the process 1
process1.start()
# Here we start process 2
process2.start()

# The join() method is used to wait for process 1 to complete
process1.join()
# It is used to wait for process 1 to complete
process2.join()

# Print if both processes are completed
print(«Both processes are finished»)

Выход:

The Cube is: 125
The Square is: 25
Both processes are finished

Объяснение:

В приведенном выше примере мы создали две функции: функция cube() вычисляет куб данного числа, а функция square() вычисляет квадрат данного числа.

Затем мы определили объект процесса класса Process, который имеет два аргумента. Первый аргумент – это target, который представляет функцию, которая должна быть выполнена, а второй аргумент – это args, который представляет аргумент, который должен быть передан внутри функции.

process1 = multiprocessing.Process(target= square, args=(5, ))
process2 = multiprocessing.Process(target= cube, args=(5, ))

Мы использовали метод start(), чтобы запустить процесс.

process1.start()
process2.start()

Как видно из выходных данных, он ожидает завершения первого процесса, а затем второго процесса. Последний оператор выполняется после завершения обоих процессов.

Классы многопроцессорной обработки Python

Модуль многопроцессорности Python предоставляет множество классов, которые обычно используются для построения параллельных программ. Мы обсудим его основные классы – Process, Queue и Lock. Мы уже обсуждали класс Process в предыдущем примере. Теперь обсудим классы Queue и Lock.

Давайте посмотрим на простой пример нахождения числа CPU, работающих в настоящее время в системе.

Пример –

import multiprocessing
print(«The number of CPU currently working in system : «, multiprocessing.cpu_count())

Выход:

('The number of CPU currently woking in system : ', 32)

Вышеуказанное количество CPU может различаться для вашего компьютера. У нас количество ядер 32.

Использование класса Queue

Мы знаем, что Queue – важная часть структуры данных. Многопроцессорность Python в точности такая же, как и очередь структуры данных, основанная на концепции «первым пришел – первым ушел». Очередь в Python обычно хранит объект и играет важную роль в обмене данными между процессами.

Queue передаются как параметр в целевой функции процесса, чтобы позволить процессу потреблять данные. Queue предоставляет функцию put() для вставки данных и функцию get() для получения данных из очередей. Рассмотрим следующий пример.

Пример –

# Importing Queue Class

from multiprocessing import Queue

fruits = ['Apple', 'Orange', 'Guava', 'Papaya', 'Banana']
count = 1
# creating a queue object
queue = Queue()
print('pushing items to the queue:')
for fr in fruits:
print('item no: ', count, ' ', fr)
queue.put(fr)
count += 1

print('
popping items from the queue:')
count = 0
while not queue.empty():
print('item no: ', count, ' ', queue.get())
count += 1

Выход:

pushing items to the queue:
('item no: ', 1, ' ', 'Apple')
('item no: ', 2, ' ', 'Orange')
('item no: ', 3, ' ', 'Guava')
('item no: ', 4, ' ', 'Papaya')
('item no: ', 5, ' ', 'Banana')

popping items from the queue:
('item no: ', 0, ' ', 'Apple')
('item no: ', 1, ' ', 'Orange')
('item no: ', 2, ' ', 'Guava')
('item no: ', 3, ' ', 'Papaya')
('item no: ', 4, ' ', 'Banana')

Объяснение:

В приведенном выше коде мы импортировали класс Queue и инициализировали список с именем fruit. Затем мы присвоили счетчик 1. Переменная счетчика будет подсчитывать общее количество элементов.

Затем мы создали объект очереди, вызвав метод Queue(). Этот объект будет использоваться для выполнения операций в Queue.

В цикле for мы вставляли элементы один за другим в очередь с помощью функции put() и увеличивали счетчик на 1 с каждой итерацией цикла.

Класс блокировки

Класс multiprocessing Lock используется для получения блокировки процесса, чтобы мы могли удерживать другой процесс для выполнения аналогичного кода до тех пор, пока блокировка не будет снята. Класс Lock выполняет в основном две задачи. Первая – получить блокировку с помощью функции accept(), а вторая – снять блокировку с помощью функции release().

Пример многопроцессорной обработки

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

Мы объединим все классы многопроцессорной обработки вместе. Давайте посмотрим на пример ниже.

Пример –

from multiprocessing import Lock, Process, Queue, current_process
import time
import queue

def jobTodo(tasks_to_perform, complete_tasks):
while True:
try:

# The try block to catch task from the queue.
# The get_nowait() function is used to
# raise queue.Empty exception if the queue is empty.

task = tasks_to_perform.get_nowait()

except queue.Empty:

break
else:

# if no exception has been raised, the else block will execute
# add the task completion

print(task)
complete_tasks.put(task + ' is done by ' + current_process().name)
time.sleep(.5)
return True

def main():
total_task = 8
total_number_of_processes = 3
tasks_to_perform = Queue()
complete_tasks = Queue()
number_of_processes = []

for i in range(total_task):
tasks_to_perform.put(«Task no » + str(i))

# defining number of processes
for w in range(total_number_of_processes):
p = Process(target=jobTodo, args=(tasks_to_perform, complete_tasks))
number_of_processes.append(p)
p.start()

# completing process
for p in number_of_processes:
p.join()

# print the output
while not complete_tasks.empty():
print(complete_tasks.get())

return True

if __name__ == '__main__':
main()

Выход:

Task no 2
Task no 5
Task no 0
Task no 3
Task no 6
Task no 1
Task no 4
Task no 7
Task no 0 is done by Process-1
Task no 1 is done by Process-3
Task no 2 is done by Process-2
Task no 3 is done by Process-1
Task no 4 is done by Process-3
Task no 5 is done by Process-2
Task no 6 is done by Process-1
Task no 7 is done by Process-3

Читайте также:  5 лучших гаджетов для отпуска

Пул многопроцессорности

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

Пример –

from multiprocessing import Pool
import time

w = ([«V», 5], [«X», 2], [«Y», 1], [«Z», 3])

def work_log(data_for_work):
print(» Process name is %s waiting time is %s seconds» % (data_for_work[0], data_for_work[1]))
time.sleep(int(data_for_work[1]))
print(» Process %s Executed.» % data_for_work[0])

def handler():
p = Pool(2)
p.map(work_log, w)

if __name__ == '__main__':
handler()

Выход:

Process name is V waiting time is 5 seconds
Process V Executed.
Process name is X waiting time is 2 seconds
Process X Executed.
Process name is Y waiting time is 1 seconds
Process Y Executed.
Process name is Z waiting time is 3 seconds
Process Z Executed.

Давайте разберемся с еще одним примером многопроцессорного пула.

Пример – 2

from multiprocessing import Pool
def fun(x):
return x*x

if __name__ == '__main__':
with Pool(5) as p:
print(p.map(fun, [1, 2, 3]))

Выход:

[1, 8, 27]

Прокси-объекты

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

Пример –

from multiprocessing import Manager
manager = Manager()
l = manager.list([i*i for i in range(10)])
print(l)
print(repr(l))
print(l[4])
print(l[2:5])

Выход:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

16
[4, 9, 16]

Прокси-объекты можно выбирать, поэтому мы можем передавать их между процессами. Эти объекты также используются для контроля над синхронизацией.

Часто используемые функции для достижения многопроцессорности

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

Метод
Описание
pipe() Функция pipe() возвращает пару объектов подключения.
run() Метод run() используется для представления действий процесса.
start() Метод start() используется для запуска процесса.
join() ([timeout]) Метод join() используется для блокировки процесса до тех пор, пока не завершится процесс, каким метод join() вызван. Timeout – необязательный аргумент.
is_alive() Возвращается, если процесс действующий.
terminate() Как следует из названия, используется для завершения процесса. Всегда помните – в Linux используется метод terminate(), для Windows мы используем метод TerminateProcess().
kill() Этот метод аналогичен terminate(), но использует сигнал SIGKILL в Unix.
close() Используется для закрытия объекта Process и освобождения всех связанных с ним ресурсов.
qsize () Возвращает приблизительный размер очереди.
empty() Если очередь пуста, возвращается True.
full() Возвращает True, если очередь заполнена.
get_await () Этот метод эквивалентен get (False).
get() Используется для получения элементов из очереди. Удаляет и возвращает элемент из очереди.
put() Этот метод используется для вставки элемента в очередь.
cpu_count() Возвращает количество работающих ЦП в системе.
current_process() Он возвращает объект Process, соответствующий текущему процессу.
parent_process() Он возвращает родительский объект Process, соответствующий текущему процессу.
task_done() Эта функция используется для указания того, что поставленная в очередь задача завершена.
join_thread() Этот метод используется для присоединения к фоновому потоку.

Мультипроцессорность на десктопе Мультипроцессорность на десктопе Изучаю Python вместе с вами, читаю, собираю и записываю информацию опытных программистов.

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

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

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

Как известно, одним из основных конструкторских замыслов для Windows была ее обязательная работа на мультипроцессорных компьютерных системах. Windows является симметричной мультипроцессорной (symmetric multiprocessing, SMP) операционной системой. В ней нет главного процессора — как операционная система, так и пользовательские потоки могут быть спланированы для работы на любом процессоре.

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

Windows также поддерживает три современных типа мультипроцессорных систем: многоядерные, гиперпотоковость (Hyper-Threading) и с технологией доступа к неоднородной памяти — NUMA (non-uniformmemoryarchitecture).

Гиперпотоковость (Hyper-Threading) является технологией, представленной компанией Intel и предоставляющей два логических процессора для каждого физического ядра.

У каждого логического процессора есть свое собственное состояние центрального процессора, но механизм исполнения команд и процессорная кэш-память у них общие.

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

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

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

Тем не менее Windows на NUMA-системе работает как SMP-система, в которой все процессоры имеют доступ ко всей памяти — при том, что обращение к локальной памяти узла осуществляется быстрее, чем к памяти, подключенной к другим узлам.

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

Мультипроцессорность на десктопе

Сравнение симметричной и асимметричной мультипроцессорной обработки

И конечно же, Windows также изначально поддерживает многоядерные системы, поскольку у этих систем есть настоящие физические ядра (они находятся на одном кристалле), исходный SMP-код в Windows считает их отдельными процессорами, за исключением некоторых задач учета использования ресурсов и идентификации (например, лицензирования), делающих различия между ядрами на одном и том же процессоре и ядрами на разных сокетах.

Изначально в Windows не предусматривался какой-либо лимит на количество процессоров, если не считать политик лицензирования, устанавливающих различия между разными версиями Windows.

Но в целях удобства и эффективности Windows занимается отслеживанием процессоров (их общего числа, простоев, занятости и других таких же подробностей) в битовой маске (маской родственности, affinity mask), в которой количество бит соответствует исходной размерности данных машины (32- или 64-разрядной), что позволяет процессору манипулировать битами непосредственно в регистре.

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

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

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

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

Как уже упоминалось, фактическое число поддерживаемых лицензированных процессоров зависит от используемой версии Windows. Это количество хранится в файле лицензионной политики системы (WindowsServiceProfilesNetworkServiceAppDataRoamingMicrosoftSoftwareProtectionPlatform okens.

dat) в виде значения политики под названием «Kernel-RegisteredProcessors».

Читайте также:  Монитор для фотографа: как выбрать, рейтинг моделей 2019-2020 года

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

Обзор архитектуры современных многоядерных процессоров

Без каких-либо особых преувеличений можно заявить, что все развитие компьютерных систем происходило и происходит под девизом «Скорость и быстрота вычислений».

Если быстродействие первой вычислительной машины ENIAC составляло всего несколько тысяч операций в секунду, то самый быстрый на ноябрь 2012 г. суперкомпьютер Titan может выполнять уже несколько квадриллионов (1015) команд.

Темп развития вычислительной техники просто впечатляет – увеличение скорости вычислений в триллионы (1012) раз немногим более чем за 60 лет! Для лучшего понимания необычности столь стремительного развития средств вычислительной техники часто приводят яркие аналогии, например: если бы автомобильная промышленность развивалась с такой же динамикой, то сейчас автомобили весили бы порядка 200 граммов и тратили бы несколько литров бензина на миллионы километров!

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

Проблема создания высокопроизводительных вычислительных систем относится к числу наиболее сложных научно-технических задач современности, и ее разрешение возможно только при всемерной концентрации усилий многих талантливых ученых и конструкторов, предполагает использование всех последних достижений науки и техники и требует значительных финансовых инвестиций. Здесь важно отметить, что при общем росте скорости вычислений в 1012 раз быстродействие самих технических средств вычислений увеличилось всего в несколько миллионов раз. И дополнительный эффект достигнут за счет введения параллелизма буквально на всех стадиях и этапах вычислений.

Не ставя целью в рамках данного курса подробное рассмотрение истории развития компьютерного параллелизма, отметим, например, организацию независимости работы разных устройств ЭВМ (процессора и устройств ввода-вывода), появление многоуровневой памяти, совершенствование архитектуры процессоров (суперскалярность, конвейерность, динамическое планирование). Дополнительная информация по истории параллелизма может быть получена, например, в [67]; здесь же выделим как принципиально важный итог – многие возможные пути совершенствования процессоров практически исчерпаны (так, возможность дальнейшего повышения тактовой частоты процессоров ограничивается рядом сложных технических проблем) и наиболее перспективное направление на данный момент времени состоит в явной организации многопроцессорности вы-числительных устройств.

Ниже будут более подробно рассмотрены основные способы организации многопроцессорности – симметричной мультипроцессорности (Symmetric Multiprocessor, SMP), одновременной многопотоковости (Simultaneous Multithreading, SMT) и многоядерности (multicore).

Организация симметричной мультипроцессорности (Symmetric Multiprocessor, SMP), когда в рамках одного вычислительного устройства имеется несколько полностью равноправных процессоров, является практически первым использованным подходом для обеспечения многопроцессорности – первые вычислительные системы такого типа стали появляться в середине 50-х – начале 60-х годов, однако массовое применение SMP систем началось только в середине 90-х годов.

Следует отметить, что SMP системы входят в группу MIMD (multi instruction multi data) вычислительных систем в соответствии с классификацией Флинна.

Поскольку эта классификация приводит к тому, что практически все виды параллельных систем (несмотря на их существенную разнородность) относятся к одной группе MIMD, для дальнейшей детализации класса MIMD предложена практически общепризнанная структурная схема [47, 99] – см.

рис. 1.1. В рамках данной схемы дальнейшее разделение типов многопроцессорных систем основывается на используемых способах организации оперативной памяти в этих системах.

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

Для дальнейшей систематики мультипроцессоров учитывается способ построения общей памяти. Возможный подход – использование единой (централизованной) общей памяти (shared memory) – см. рис. 1.2.

Такой подход обеспечивает однородный доступ к памяти (uniform memory access, UMA) и служит основой для построения векторных параллельных процессоров (parallel vector processor, PVP) и симметричных мультипроцессоров (symmetric multiprocessor, SMP).

Среди примеров первой группы – суперкомпьютер Cray T90, ко второй группе относятся IBM Server, Sun StarFire, HP Superdome, SGI Origin и др.

Одной из основных проблем, которые возникают при организации параллельных вычислений на такого типа системах, является обеспечение информационной целостности (когерентности) кэшей (cache coherence problem).

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

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

Существует две методики дублирования данных их кэша в оперативную память: обратная запись (write back) и сквозная запись (write through). При использовании сквозной записи в кэш все записываемые данные сразу дублируются в оперативной памяти.

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

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

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

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

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

Блокирование кэширования во всех остальных случаях (когда информационной целостности данных ничто не угрожает) приведёт к необоснованному снижению быстродействия программы.

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

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

Существует множество аппаратных методов обеспечения информационной целостности данных. Более подробно об этом можно прочитать в [1]. Мы рассмотрим метод с использованием про-токола MESI.

  • Протокол MESI основан на введении четырёх состояний для каждой строки кэша:
  • modified (строка была изменена и изменения не отражены в оперативной памяти),
  • exclusive (данные в строке кэша и оперативной памяти одинаковы, в остальных кэшах этих данных нет),
  • shared (данные в строке кэша и оперативной памяти одинаковы, в каких-то других кэшах этих данные тоже присутствуют),
  • invalid (строка содержит недостоверные данные).

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

Если строка имела состояние exclusive или modified, то текущий процессор имеет исключительное право владения этой строкой и изменяет состояние строки на modified.

Если строка имела состояние shared, то состояние копи строки в других кэшах меняется на invalid, а текущий процессор обновляет данных в кэше и изменяет состояние строки на modified. Операции чтения при промахе может выполняться по-разному в зависимости от состояния других кэшей.

Если в каком-либо кэше содержится копия строки в состоянии exclusive или shared, то данные загружаются в кэш из оперативной памяти, а строка помечается как shared в этих кэшах.

Если в каком-либо кэше содержится копия строки в состоянии modified, то этот кэш заблокирует чтение из памяти и инициирует запись строки в оперативную память, после чего изменит состояние строки на shared. Если ни в одном из кэшей нет копии строки, то блок данных загружается из оперативной памяти, а строка помечается как exclusive.

Читайте также:  Verbatim выпустила "вечные" оптические диски M-Disc

Операция записи при промахе инициирует чтение блока данных из оперативной памяти и устанавливает состояние строки в значение modified. При этом, если в каком-либо кэше находилась копия данных в состоянии modified, то сначала данные из этого кэша записываются в оперативную память, а строка помечается как invalid. Все кэши, которые содержали копию данных в состоянии shared или exclusive переходят в состояние invalid.

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

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

Для обеспечения когерентности кэшей L1 используются более сложные алгоритмы (особенно при использовании обратной записи).

На данный момент широко используется модифицированный протокол MESI в котором добавлено дополнительное состояние, позволяющее эффективнее решать проблему когерентности данных за счёт загрузки требуемых данных из соседнего кэша, а не из оперативной памяти. Intel использует протокол MESIF, AMD – MOESI.

Наличие общих данных при выполнении параллельных вычислений приводит к необходимости синхронизации взаимодействия одновременно выполняемых потоков команд.

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

Задачи взаимоисключения и синхронизации относятся к числу классических проблем, и их рассмотрение при разработке параллельных программ является одним из основных вопросов параллельного программирования.

Общий доступ к данным может быть обеспечен и при физически распределенной памяти (при этом, естественно, длительность доступа уже не будет одинаковой для всех элементов памяти) – см. рис. 1.2. Такой подход именуется как неоднородный доступ к памяти (non-uniform memory access, или NUMA). Среди систем с таким типом памяти выделяют:

Мультипроцессорность на десктопе
Рис. 1.2. Архитектура многопроцессорных систем с общей (разделяемой) памятью: системы с (а) однородным и (б) неоднородным доступом к памяти

  • Системы, в которых для представления данных используется только локальная кэш-память имеющихся процессоров (cache-only memory architecture, или COMA); примерами таких систем являются KSR-1 и DDM.
  • Системы, в которых обеспечивается когерентность локальных кэшей разных процессоров (cache-coherent NUMA, или CC-NUMA); среди систем данного типа – SGI Origin 2000, Sun HPC 10000, IBM/Sequent NUMA-Q 2000.
  • Системы, в которых обеспечивается общий доступ к локальной памяти разных процессоров без поддержки на аппаратном уровне когерентности кэша (non-cache coherent NUMA, или NCC-NUMA); к данному типу относится, например, система Cray T3E.

Использование распределенной общей памяти (distributed shared memory, DSM) упрощает проблемы создания мультипроцессоров (известны примеры систем с несколькими тысячами процессоров), однако возникающие при этом проблемы эффективного использования распределенной памяти (время доступа к локальной и удаленной памяти может различаться на несколько порядков) приводят к существенному повышению сложности параллельного программирования.

7) Многопоточность против многопроцессорности

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

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

В этом уроке вы узнаете:

Что такое многопоточность?

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

Характеристики многопроцессорной обработки

Вот основные функции многопроцессорной обработки:

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

Характеристики многопоточности

Вот важные аспекты многопоточности:

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

Вот важные различия между многопроцессорностью и многопоточностью.

параметр
многопроцессорная обработка
Многопоточность
основной Многопроцессорность помогает вам увеличить вычислительную мощность. Многопоточность помогает вам создавать вычислительные потоки одного процесса для увеличения вычислительной мощности.
выполнение Это позволяет вам выполнять несколько процессов одновременно. Несколько потоков одного процесса выполняются одновременно.
Переключение процессора В многопроцессорной обработке ЦП должен переключаться между несколькими программами, чтобы было похоже, что несколько программ выполняются одновременно. В многопоточности ЦП должен переключаться между несколькими потоками, чтобы создать впечатление, что все потоки работают одновременно.
Создание Создание процесса происходит медленно и зависит от ресурсов. Создание потока экономно по времени и ресурсам.
классификация Многопроцессорная обработка может быть симметричной или асимметричной. Многопоточность не классифицируется.
Память Многопроцессорная обработка выделяет отдельную память и ресурсы для каждого процесса или программы. Многопоточные потоки, принадлежащие одному и тому же процессу, используют ту же память и ресурсы, что и у процесса.
Травление объектов Многопоточность позволяет избежать травления. Многопроцессорная обработка основывается на выделении объектов в памяти для отправки другим процессам.
программа Многопроцессорная система позволяет выполнять несколько программ и задач. Многопоточная система выполняет несколько потоков одинаковых или разных процессов.
Затраченное время Меньше времени уходит на обработку задания. Умеренное количество времени уходит на обработку задания.

Преимущество многопроцессорности

Вот минусы / плюсы мультипроцессинга:

  • Самым большим преимуществом многопроцессорной системы является то, что она помогает выполнять больше работы в более короткие сроки.
  • Код обычно прост.
  • Использует преимущества нескольких процессоров и ядер
  • Помогает избежать ограничений GIL для CPython
  • Удалите примитивы синхронизации, если только вы не используете общую память.
  • Дочерние процессы в основном прерываются / убиваются
  • Это поможет вам выполнить работу в более короткие сроки.
  • Эти типы систем следует использовать, когда для обработки большого объема данных требуется очень высокая скорость.
  • Многопроцессорные системы экономят деньги по сравнению с однопроцессорными системами, поскольку процессоры могут совместно использовать периферийные устройства и источники питания.

Преимущество многопоточности

Вот преимущества / преимущества многопоточности:

  • Потоки имеют одинаковое адресное пространство
  • Потоки имеют малый вес и занимают мало памяти
  • Стоимость связи между потоками низкая.
  • Доступ к состоянию памяти из другого контекста проще
  • Это позволяет легко создавать адаптивные интерфейсы
  • Идеальный вариант для приложений ввода-вывода
  • Занимает меньше времени для переключения между двумя потоками в общей памяти и время для завершения
  • Потоки запускаются быстрее, чем процессы, а также при переключении задач.
  • Все потоки имеют общий пул памяти процесса, что очень полезно.
  • Занимает меньше времени для создания нового потока в существующем процессе, чем новый процесс

Недостаток многопроцессорности

Вот минусы / минусы при использовании многопроцессорной операционной системы

  • IPC (межпроцессное взаимодействие) довольно сложный с большими накладными расходами
  • Имеет больший объем памяти

Недостаток многопоточности

Вот минусы / недостатки использования многопоточной системы:

  • Многопоточная система не прерываемая / убиваемая
  • Если не следует очереди команд и модели сообщений, тогда необходимо ручное использование синхронизации, что становится необходимостью
  • Код, как правило, сложнее понять, и его потенциал в условиях гонки резко возрастает.

Ключевые ОТЛИЧИЯ:

  • Многопроцессорная система имеет более двух процессоров, тогда как многопоточность – это метод выполнения программы, который позволяет одному процессу иметь несколько сегментов кода.
  • Многопроцессорная обработка повышает надежность системы, тогда как в многопоточном процессе каждый поток работает параллельно друг другу.
  • Многопроцессорность помогает вам увеличить вычислительную мощность, тогда как многопоточность помогает создавать вычислительные потоки одного процесса.
  • В многопроцессорной обработке процесс создается медленно и зависит от ресурсов, тогда как в многопрограммировании создание потока экономично по времени и ресурсам.
  • Многопоточность предотвращает выборку, тогда как многопроцессорная обработка полагается на выборку объектов в памяти для отправки другим процессам.
  • Многопроцессорная система занимает меньше времени, в то время как для обработки работы требуется умеренное количество времени.
Ссылка на основную публикацию
Adblock
detector