Відповіді:
Це досить інтуїтивно:
Програма пов'язана з процесором, якщо вона пішла б швидше, якщо процесор був би швидшим, тобто вона витрачає більшість свого часу просто на використання процесора (роблячи розрахунки). Програма, яка обчислює нові цифри π, як правило, пов'язана з процесором, це просто стискання чисел.
Програма пов'язана вводу / виводу, якщо вона пішла швидше, якби підсистема вводу-виводу була швидшою. Яка саме система вводу / виводу має на увазі, може змінюватися; Я, як правило, асоціюю його з диском, але звичайно також мережа чи спілкування загалом. Програма, яка переглядає величезний файл для деяких даних, може стати прив'язаною до вводу / виводу, оскільки вузьким місцем цього місця є зчитування даних з диска (насправді цей приклад, мабуть, є старомодним у ці дні із сотнями МБ / с що надходять із SSD).
CPU Bound - швидкість, з якою прогресує процес, обмежена швидкістю процесора. Завдання, яке виконує обчислення на невеликому наборі чисел, наприклад, множення малих матриць, ймовірно, пов'язане з процесором.
Межа вводу / виводу - швидкість, з якою прогресує процес, обмежена швидкістю підсистеми вводу / виводу. Завдання, яке обробляє дані з диска, наприклад, підрахунок кількості рядків у файлі, ймовірно, пов'язане введення-виведення.
Обмежена пам'ять означає, що швидкість, з якою прогресує процес, обмежена наявним обсягом пам'яті та швидкістю доступу до цієї пам'яті. Завданням, яке обробляє велику кількість даних у пам'яті, наприклад, множення великих матриць, ймовірно, є межею пам'яті.
Зв'язаний кеш - означає швидкість, з якою прогрес процесу обмежується кількістю та швидкістю доступного кешу. Завдання, яке просто обробляє більше даних, ніж вміщається в кеш, буде кеш-зв’язком.
Введення вводу / виводу буде повільніше, ніж обмеження пам'яті, буде повільніше, ніж кеш-пам'ять буде повільніше, ніж пов'язане з процесором.
Рішення обмеження вводу / виводу не обов'язково отримувати більше пам’яті. У деяких ситуаціях алгоритм доступу може бути розроблений на основі обмежень вводу / виводу, пам'яті або кешу. Дивіться кешові очевидні алгоритми .
Багатопотоковість
У цій відповіді я досліджу один важливий випадок використання розрізнення між процесором та обмеженням роботи IO: при написанні багатопотокового коду.
Приклад зв’язаного вводу / виводу оперативної пам'яті: Сума вектора
Розглянемо програму, яка підсумовує всі значення одного вектора:
#define SIZE 1000000000
unsigned int is[SIZE];
unsigned int sum = 0;
size_t i = 0;
for (i = 0; i < SIZE; i++)
/* Each one of those requires a RAM access! */
sum += is[i]
Паралельне розуміння того, що розділення масиву порівну для кожного з ваших ядер є обмеженою корисністю на звичайних сучасних настільних комп’ютерах.
Наприклад, на моєму ноутбуці Ubuntu 19.04, Lenovo ThinkPad P51 з процесором: процесор Intel Core i7-7820HQ (4 ядра / 8 потоків), оперативна пам'ять: 2x Samsung M471A2K43BB1-CRC (2x 16GiB), я отримую такі результати:
Зауважте, що існує велика дисперсія між запуском. Але я не можу значно збільшити розмір масиву, оскільки я вже на 8GiB, і я не в настрої для статистики на кількох пробігах сьогодні. Однак це здавалося типовим пробігом після багатьох ручних пробіжок.
Код орієнтиру:
pthread
Вихідний код POSIX C, використаний у графіку.
А ось версія C ++, яка дає аналогічні результати.
Я не знаю достатньої архітектури комп’ютера, щоб повністю пояснити форму кривої, але одне зрозуміло: обчислення не стають на 8 разів швидшими, як наївно очікувалося завдяки мені, використовуючи всі мої 8 ниток! Чомусь 2 та 3 нитки були оптимальними, а додавання більше просто робить речі набагато повільнішими.
Порівняйте це з роботою, пов'язаною з процесором, яка насправді стає в 8 разів швидшою: Що означають "реальний", "користувач" і "sys" у результаті часу (1)?
Причина в тому, що всі процесори поділяють єдину шину пам'яті, яка з'єднується з ОЗУ:
CPU 1 --\ Bus +-----+
CPU 2 ---\__________| RAM |
... ---/ +-----+
CPU N --/
тому шина пам'яті швидко стає вузьким місцем, а не процесором.
Це трапляється тому, що додавання двох чисел займає один цикл процесора, читання пам'яті займає близько 100 циклів процесора в апараті 2016 року.
Таким чином, робота процесора, виконана на байт вхідних даних, занадто мала, і ми називаємо це процесом, пов'язаним з IO.
Єдиним способом подальшого прискорення обчислень було б прискорити доступ до індивідуальної пам'яті за допомогою нового апаратного забезпечення пам'яті, наприклад, багатоканальної пам'яті .
Наприклад, оновлення до більш швидкого тактового процесора було б не дуже корисним.
Інші приклади
множення матриці пов'язане з процесором на ОЗП та графічних процесорах. Вхід містить:
2 * N**2
числа, але:
N ** 3
множення зроблено, і цього достатньо, щоб паралелізація була вартим для практичного великого Н.
Ось чому існують паралельні бібліотеки множення матриць процесора, такі як:
Використання кеша значною мірою залежить від швидкості реалізації. Дивіться для прикладу цей дидактичний приклад порівняння GPU .
Дивись також:
Мережа - прототипний приклад, пов'язаний з IO.
Навіть коли ми надсилаємо один байт даних, це все ще потребує значного часу, щоб досягти свого призначення.
Паралелізація невеликих мережевих запитів, таких як HTTP-запити, може принести величезну ефективність.
Якщо мережа вже на повній потужності (наприклад, завантаження торента), паралелізація все ще може збільшити покращення затримки (наприклад, ви можете завантажити веб-сторінку "одночасно").
Фіктивна операція, пов’язана з процесором C ++, яка займає одне число і дуже сильно стискає її:
Сортування здається ЦП на основі наступного експерименту: Чи реалізовані C ++ 17 паралельних алгоритмів? що показало покращення продуктивності в 4 рази для паралельного сортування, але я також хотів би мати більш теоретичне підтвердження
Як дізнатися, чи є ви CPU чи IO
IO без оперативної пам'яті, пов'язаний як диск, мережа:, ps aux
то theck if CPU% / 100 < n threads
. Якщо так, ви пов’язані IO, наприклад, блокування read
s просто чекає даних, і планувальник пропускає цей процес. Потім використовуйте додаткові інструменти, як sudo iotop
вирішити, в якій саме IO проблеми.
Або, якщо виконання швидке, і ви параметризуєте кількість потоків, ви легко бачите, time
що ефективність покращується, коли кількість потоків збільшується для роботи, пов'язаної з процесором: Що означають "реальні", "користувачі" та "sys" в вихід часу (1)?
ОЗУ-IO пов'язані: важче сказати, оскільки час очікування оперативної пам'яті включено в CPU%
вимірювання, див. Також:
Деякі варіанти:
GPU
Під час першого перенесення вхідних даних із звичайної оперативної пам’яті, що читається з процесора, в GPU є вузол IO.
Отже, графічні процесори можуть бути кращими, ніж процесори для додатків, пов'язаних з процесором.
Однак, як тільки дані передаються в GPU, вони можуть працювати на цих байтах швидше, ніж може процесор, тому що GPU:
має більшу локалізацію даних, ніж у більшості систем процесора, і тому до деяких ядер можна отримати доступ швидше, ніж інші
використовує паралелізм даних та жертвує затримкою, просто пропускаючи будь-які дані, не готові до негайного використання.
Оскільки GPU має працювати над великими паралельними вхідними даними, краще просто перейти до наступних даних, які можуть бути доступні, а не чекати, коли поточні дані стануть доступними, і заблокувати всі інші операції, такі як центральний процесор.
Тому GPU може бути швидшим за процесор, якщо ваша програма:
Цей вибір дизайну спочатку орієнтований на застосування 3D-рендерінга, основні етапи якого показані на сторінці Що таке шейдери в OpenGL і для чого вони потрібні?
і тому ми робимо висновок, що ці програми пов'язані з процесором.
З появою програмованого GPGPU ми можемо спостерігати кілька додатків GPGPU, які слугують прикладом операцій, пов'язаних з процесором:
Обробка зображень за допомогою шейдерів GLSL?
Локальні операції з обробки зображень, такі як фільтр розмиття, мають дуже паралельний характер.
Чи можливо побудувати теплову карту з точкових даних з 60 разів на секунду?
Складання графіків теплової карти, якщо побудована функція є досить складною.
https://www.youtube.com/watch?v=fE0P6H8eK4I "Динаміка рідини в режимі реального часу: процесор проти GPU" від Jesús Martín Berlanga
Розв’язування часткових диференціальних рівнянь, таких як рівняння Нав'є Стокса у динаміці рідини:
Дивись також:
Глобальний інтеграторний замок CPython (GIL)
Як швидке тематичне дослідження, я хочу вказати на Блокування інтерпретатора Python Global (GIL): Що таке блокування глобального інтерпретатора (GIL) у CPython?
Ця деталь реалізації CPython запобігає ефективному використанню декількох потоків Python із використанням роботи, пов'язаної з процесором. Документи CPython кажуть:
Деталі реалізації CPython: У CPython, завдяки глобальному блоку інтерпретаторів, лише один потік може виконувати код Python одночасно (навіть якщо певні бібліотеки, орієнтовані на ефективність, можуть подолати це обмеження). Якщо ви хочете, щоб ваша програма краще використовувала обчислювальні ресурси багатоядерних машин, вам рекомендується використовувати
multiprocessing
абоconcurrent.futures.ProcessPoolExecutor
. Однак нарізка різьби все ще є відповідною моделлю, якщо ви хочете виконувати кілька завдань, пов'язаних з входом / виводом одночасно.
Отже, тут ми маємо приклад, коли вміст, пов'язаний з процесором, не підходить, а введення / виведення є.
Прив’язаний до процесора означає, що програма є вузьким процесором або центральним процесорним блоком, тоді як пов'язана введення / виведення означає, що програма є вузьким місцем вводу / виводу або введення / виводу, наприклад, читання або запис на диск, мережу тощо.
Взагалі, під час оптимізації комп'ютерних програм намагаються знайти вузьке місце і усунути його. Знаючи, що ваша програма пов'язана з процесором, допомагає, так що не потрібно зайве оптимізувати щось інше.
[І під «вузьким місцем» я маю на увазі те, що робить вашу програму повільнішою, ніж це було б інакше.]
Ще один спосіб сформулювати ту саму ідею:
Якщо прискорення процесора не прискорює вашу програму, можливо, це пов'язано з входом / виводом.
Якщо прискорення вводу / виводу (наприклад, використання більш швидкого диска) не допомагає, програма може бути пов'язана з процесором.
(Я використовував "можливо", тому що вам потрібно враховувати інші ресурси. Пам'ять - один із прикладів.)
Коли ваша програма чекає вводу / виводу (тобто читання / запис диска або мережевого читання / запису тощо), процесор може виконувати інші завдання, навіть якщо ваша програма зупинена. Швидкість вашої програми в основному буде залежати від того, наскільки швидко цей IO може статися, і якщо ви хочете прискорити його, вам потрібно буде прискорити введення-виведення.
Якщо у вашій програмі працює багато програмних інструкцій і не чекають вводу / виводу, тоді, як кажуть, вона пов'язана з процесором. Прискорення процесора дозволить програмі працювати швидше.
У будь-якому випадку ключовим прискоренням програми може бути не пришвидшення обладнання, а оптимізація програми для зменшення кількості вводу-виводу або процесора, або необхідність введення-виводу, в той час як це також робить процесор інтенсивним речі.
Обмеження вводу / виводу означає умову, в якій час, необхідний для завершення обчислення, визначається головним чином періодом, витраченим на очікування завершення операцій введення / виведення.
Це протилежне завдання, пов'язаному з процесором. Ця обставина виникає, коли швидкість, з якою запитуються дані, менша, ніж швидкість її споживання, або, іншими словами, більше часу витрачається на запит даних, ніж на їх обробку.
Подивіться, що говорить Microsoft.
Ядром асинхронного програмування є об'єкти Task and Task, які моделюють асинхронні операції. Вони підтримуються ключовими словами async і очікують. Модель у більшості випадків досить проста:
Щодо коду, пов'язаного з входом / виводу, ви очікуєте операції, яка повертає завдання або завдання всередині методу асинхронізації.
Для коду, пов'язаного з процесором, ви очікуєте операції, яка розпочалася на фоновому потоці методом Task.Run.
Ключове слово в очікуванні - це те, де відбувається магія. Це дає контроль за абонентом методу, який виконується в очікуванні, і в кінцевому підсумку дозволяє користувальницькому інтерфейсу бути чуйним або послугою бути еластичною.
Приклад введення-виведення: Завантаження даних із веб-сервісу
private readonly HttpClient _httpClient = new HttpClient();
downloadButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI as the request
// from the web service is happening.
//
// The UI thread is now free to perform other work.
var stringData = await _httpClient.GetStringAsync(URL);
DoSomethingWithData(stringData);
};
Приклад, пов'язаний з процесором: проведення розрахунку для гри
private DamageResult CalculateDamageDone()
{
// Code omitted:
//
// Does an expensive calculation and returns
// the result of that calculation.
}
calculateButton.Clicked += async (o, e) =>
{
// This line will yield control to the UI while CalculateDamageDone()
// performs its work. The UI thread is free to perform other work.
var damageResult = await Task.Run(() => CalculateDamageDone());
DisplayDamage(damageResult);
};
Наведені вище приклади показали, як можна використовувати асинхронізацію та очікувати роботи, пов'язаної з входом / виведенням та процесором. Це ключове значення, яке ви можете визначити, коли робота, яку вам потрібно виконати, пов'язана з входом / виведенням або процесором, оскільки це може сильно вплинути на продуктивність вашого коду і потенційно може призвести до неправильного використання певних конструкцій.
Ось два питання, які слід задати перед тим, як написати будь-який код:
Чи буде ваш код "чекати" чогось, наприклад, даних із бази даних?
- Якщо ваша відповідь "так", то ваша робота пов'язана з I / O.
Чи буде ваш код виконувати дуже дорогі обчислення?
- Якщо ви відповіли "так", то ваша робота пов'язана з процесором.
Якщо у вас робота пов'язана з входом / виводом , використовуйте функцію async і чекайте без завдання Task.Run . Не слід використовувати паралельну бібліотеку завдань. Причина цього викладена в статті Async in Depth .
Якщо у вас робота пов'язана з процесором, і ви дбаєте про чуйність, використовуйте функцію async і чекайте, але породжуйте роботу в іншому потоці за допомогою Task.Run. Якщо робота підходить для одночасності та паралелізму, слід також розглянути можливість використання бібліотеки паралельних завдань .
Додаток пов'язане з процесором, коли продуктивність арифметичної / логічної / плаваючої точки (A / L / FP) під час виконання здебільшого близька до теоретичних пікових показників процесора (дані, надані виробником та визначаються характеристиками процесор: кількість ядер, частота, регістри, ALU, FPU та ін.).
Ефективність виклику дуже складно досягти в реальних програмах, бо не можна сказати, що це неможливо. Більшість додатків отримують доступ до пам'яті в різних частинах виконання, а процесор не виконує операції A / L / FP протягом декількох циклів. Це називається обмеженням фон Неймана через відстань, що існує між пам'яттю та процесором.
Якщо ви хочете бути поруч із максимальною продуктивністю процесора, стратегією може бути спроба повторно використовувати більшість даних у кеш-пам'яті, щоб уникнути необхідності використання даних з основної пам'яті. Алгоритм, який використовує цю особливість, - це множення матриці-матриці (якщо обидві матриці можуть зберігатися в кеш-пам'яті). Це трапляється тому, що якщо матриці мають розмір, n x n
то вам потрібно робити 2 n^3
операції, використовуючи лише 2 n^2
FP номери даних. З іншого боку, матричне додавання, наприклад, є менш прив'язаним процесором або більш прив'язаним до пам'яті додатком, ніж множення матриці, оскільки воно вимагає лише n^2
FLOP з тими ж даними.
На наступному малюнку показані FLOP, отримані за допомогою наївних алгоритмів додавання матриць та множення матриць в Intel i5-9300H:
Зауважимо, що як і очікувалося, ефективність множення матриці більша, ніж додавання матриці. Ці результати можна відтворити за допомогою запущених test/gemm
та test/matadd
доступних у цьому сховищі .
Пропоную також переглянути відео Дж. Донгарра про цей ефект.
Зв'язаний процес вводу / виводу: - Якщо більша частина часу процесу проходить у стані вводу / виводу, то процес є ai / o пов'язаним процесом. Приклад: -kalculator, Internet Explorer
Процес, пов'язаний з процесором: - Якщо більша частина життя процесу проводиться на процесорі, то це процес, пов'язаний з процесором.