Как на самом деле работает параллелизм?

Разбираюсь в основах, как CPU параллельно обрабатывает операции. Необходима помощь (можно ссылками на литературу/статьи) в разборе данной темы.

Текущее понимание
1. У нас есть CPU с, допустим, 4 ядрами. В моем текущем понимании это означает возможность выполнять истинно параллельно 4 потока операций.

2. Если у нас есть технология HyperThreading/SMT, то в некоторых случаях (допустим, когда операция ожидает подгрузку данных из L3-кеша или ОЗУ), ядро может обрабатывать второй конвейер с операциями. Итого имеем 8 логических ядер.
Вопрос 1: правильно ли я понимаю, что 2 конвейера с операциями повышают степень параллелизма каждого ядра (не линейно, конечно)?

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

4. Далее у нас появляется еще одна абстракция - потоки. Потоки - легковесные процессы, которые имеют общую память и более легкие контексты. В связи с этим их быстрее создавать и переключаться между ними, что экономит ресурсы компьютера.
Вопрос 2: правильно ли я понимаю, что с введением потоком "выполнение процесса" автоматически подразумевает под собой выполнение хотя бы одного потока на ядре?
Вопрос 3: правильно ли я понимаю, что пока работает какой-то поток/процесс, они отправляют по общей шине событий операции в конвейеры ядер?
Вопрос 4: существуют userspace-потоки и kernelspace-потоки. Как я понял, разница в том, что в первом случае мы как-то на уровне приложения создаем свои потоки, которые ядра воспринимают как один процесс и ничего не знают в отдельности про каждый поток. Из-за этого такие потоки физически выполняются по очереди. По сути, обычный механизм кооперативной многозадачности без выигрыша в скорости вычисления (т.к. параллелизма тут нет). Если так, то для чего такие потоки нужны (чем они полезны) и как их реализовать, допустим, в Python?
Вопрос 5: kernelspace-потоки выполняются напрямую на ядрах и имеют расширенный контекст. Они занимают больше памяти и переключение в kernel-режим обходится дороже, однако здесь мы приобретаем настоящий параллелизм, и тут выгода понятна. Насколько я понял, такие потоки и создают все встроенные библиотеки (threading в Python, например). Верно ли это утверждение?

Ну и финальный вопрос: в моем текущем понимании каждое ядро "играет в пошаговую стратегию". Т.е. каждое ядро дает какое-то время поработать процессу, затем отдает управление ОС и та уже выбирает следующий поток. Если так, то параллелизм здесь на лицо. Если нет, тогда я вообще не понимаю, как это все устроено...

Заранее большое спасибо за ответы.
  • Вопрос задан
  • 905 просмотров
Решения вопроса 3
leahch
@leahch
3D специалист. Dолго, Dорого, Dерьмово.
Ну, книги Вам уже посоветовал @firedragon
Я же хочу вкратце ответить на этот замечательный вопрос.
Давайте разберемся с одним CPU без потоков...
Когда процессоры были большими, а люди... В общем, на заре компухтеров был только один поток, и чтобы получить многозадачность, придумали ОС с вытеснением задач.
Смысл в том, что когда завершается "программа", то запускается следующая в очереди (очередь с приоритетом). Задача работает до тех пор, пока не завершится.

Так как задача иногда могла работать очень долго, и ничего не делать во время операций ввода-вывода то придумали прерывания, чтобы ввод-вывод сигнализировал о проделанной операции (окончании печати например, или нажатии клавиш). Тогда появились операционные системы с вытеснением задач по прерыванию. Здравствуйте мейнфреймы!

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

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

Ах, да, молодость процессоров - одно прерывание на все сразу :) И крутись, как хочешь :)
Ответ написан
firedragon
@firedragon
Не джун-мидл-сеньор, а трус-балбес-бывалый.
Хм тут вам помогут книги
Эндрю Таненбаум
Торвальдса
Русиновича
Helen Custer , David N Cutler
David A. Solomon
https://slideplayer.com/slide/6865915/

Гляньте вот на эту презентацию https://www.hse.ru/data/2010/12/24/1223886397/%D0%...

А вот тут прикладная часть
https://basis.gnulinux.pro/ru/latest/basis/15/15._...

В общем тема большая и гуглить вам много, но основных авторов я дал
Ответ написан
Vamp
@Vamp
Вопрос 1. Два конвейера позволяют только сократить накладные расходы на context switch. Параллелизм это не увеличивает, так как вычислительное ядро по-прежнему одно.

Вопрос 2. Верно. Разделите понятия процесс и поток. Думайте о процессе как о контейнере для потоков, а не как об активной сущности, которая исполняет код. В каждом процессе есть как минимум 1 поток. Все без исключения программы стартуют как однопоточный процесс, который имеет возможность превратиться в многопоточный, заспавнив ещё потоков. Ну и, соответственно, ОС шедулит потоки, а не процессы.

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

Вопрос 4. Kernelspace и userspace потоки отличаются только тем, в каких кольцах они исполняются. Kernel потоки исполняются в привилегированных кольцах, а user потоки в непривилегированных. Во всём остальном эти потоки ничем друг от друга не отличаются. Кольца ограничивают набор процессорных инструкций и системных вызовов, которые поток может использовать. Например, чтение файла с диска - это привилегированная операция (так как требует прямого доступа к девайсу), поэтому когда обычный userspace поток хочет прочитать файл, ОС приостанавливает userspace поток и передаёт управление kernelspace потоку, у которого достаточно привилегий на выполнение данной операции.

Кооперативную многозадачность можно сделать и самому. Концепция называется green threads. В языках программирования встречается уже готовая реализация грин тредов, только называется иначе: корутины (python), горутины (go), виртуальные потоки (java). Все они делают одно и то же - реализуют кооперативную многозадачность в userspace. При большом желании можно написать и свою реализацию грин тредов без использования уже готовых языковых инструментов. Хотя практического смысла в этом мало (кроме нарабатывания опыта).

Вопрос 5. Думаю, ответ на этот вопрос очевиден из ответа на предыдущий.

И всё же под "kernel" обычно подразумевают ядро ОС, а не ядро процессора (которое называют core, а не kernel). Единственный способ запустить поток в kernelspace - написать модуль/драйвер ядра. Обычные программы так не могут.

Финальный вопрос. Правильно понимаете. Вот только "пошаговая стратегия" - это про многозадачность, а не про параллелизм. Процессор с одним ядром многозадачным быть может. Параллельным - нет.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
17 нояб. 2024, в 18:17
1500 руб./за проект
17 нояб. 2024, в 17:48
3000 руб./за проект