Це дві фрази, які описують одне й те саме з (зовсім трохи) різних точок зору. Паралельне програмування описує ситуацію з точки зору обладнання - паралельно працюють принаймні два процесори (можливо, в рамках одного фізичного пакету). Одночасне програмування описує речі більше з точки зору програмного забезпечення - дві або більше дій можуть відбуватися в один і той же час (одночасно).
Проблема тут полягає в тому, що люди намагаються використовувати ці дві фрази, щоб зробити чітке розрізнення, коли жодної насправді не існує. Реальність полягає в тому, що подільна лінія, яку вони намагаються провести, була нечіткою і невиразною протягом десятиліть, а з часом стає все більш невиразною.
Те, що вони намагаються обговорити, це той факт, що колись у більшості комп'ютерів був лише один процесор. Коли ви виконували кілька процесів (або потоків) на цьому єдиному процесорі, CPU реально виконував лише одну інструкцію з одного з цих потоків одночасно. Поява паралельності було ілюзією - перемикання процесора між виконанням інструкцій з різних потоків досить швидко, що для людського сприйняття (до якого що-небудь менше 100 мс або так виглядає миттєво) виглядало так, ніби він робить багато справ одночасно.
Очевидним контрастом цього є комп'ютер з декількома процесорами, або процесор з декількома ядрами, тому машина виконує інструкції з декількох потоків та / або процесів точно в один і той же час; код, який виконує один, не може / не має жодного впливу на виконання коду в іншому.
Тепер проблема: такого чистого розмежування майже ніколи не існувало. Дизайнери комп’ютерів насправді досить розумні, тому вони давно помітили, що (наприклад), коли вам потрібно прочитати деякі дані з пристрою вводу / виводу, такого як диск, потрібно багато часу (з точки зору циклу процесора), щоб закінчити. Замість того, щоб залишати процесор у режимі очікування, поки це сталося, вони з'ясували різні способи дозволити одному процесу / потоку зробити запит вводу / виводу, і дозволити коду з якогось іншого процесу / потоку виконуватись на процесорі, поки запит вводу / виводу завершений.
Отже, задовго до того, як багатоядерні процесори стали нормою, у нас були операції з декількох потоків, що відбуваються паралельно.
Це лише верхівка айсберга. Десятиліття тому комп'ютери також почали забезпечувати інший рівень паралелізму. Знову ж таки, будучи досить розумними людьми, дизайнери комп’ютерів помітили, що у багатьох випадках вони мали інструкції, які не впливали один на одного, тому було можливо виконати більше однієї інструкції з одного потоку одночасно. Одним із ранніх прикладів, які стали досить відомими, були дані Control Control 6600. Це був (з досить широким відривом) найшвидший комп'ютер на землі, коли він був представлений у 1964 році - і значна частина тієї ж базової архітектури залишається у використанні і сьогодні. Він відслідковував ресурси, що використовуються кожною інструкцією, і мав набір одиниць виконання, які виконували інструкції, як тільки ресурси, від яких вони залежали, стали доступними, дуже схожі на дизайн останніх процесорів Intel / AMD.
Але (як говорили рекламні ролики) чекати - це ще не все. Є ще один елемент дизайну, який додасть ще більше плутанини. Дано досить багато різних імен (наприклад, "Hyperthreading", "SMT", "CMP"), але всі вони посилаються на одну і ту ж основну ідею: процесор, який може виконувати кілька потоків одночасно, використовуючи комбінацію деяких ресурсів, є незалежними для кожного потоку та деякими ресурсами, які спільно використовуються між потоками. У типовому випадку це поєднується з паралелізмом рівня інструкцій, описаним вище. Для цього у нас є два (або більше) наборів архітектурних регістрів. Тоді у нас є набір одиниць виконання, які можуть виконувати інструкції, як тільки потрібні ресурси стануть доступними.
Тоді, звичайно, ми потрапляємо до сучасних систем з декількома ядрами. Тут все очевидно, правда? У нас є N (десь від 2 до 256 або більше, на даний момент) окремих ядер, які можуть виконувати вказівки одночасно, тому у нас є чіткий випадок реального паралелізму - виконання інструкцій в одному процесі / потоці не ' t впливати на виконання інструкцій в іншому.
Ну, начебто. Навіть у нас є деякі незалежні ресурси (регістри, одиниці виконання, принаймні один рівень кешу) та деякі спільні ресурси (як правило, принаймні найнижчий рівень кешу, і, безумовно, контролери пам'яті та пропускна здатність пам’яті).
Підсумовуючи: прості сценарії люблять контрастувати між спільними ресурсами та незалежними ресурсами практично ніколи не трапляються в реальному житті. З усіма спільними ресурсами ми закінчуємо щось подібне до MS-DOS, де ми можемо запускати лише одну програму за один раз, і нам потрібно припинити запуск однієї, перш ніж ми можемо запустити іншу. Маючи повністю незалежні ресурси, у нас є N комп’ютерів, що працюють під управлінням MS-DOS (навіть без мережі для їх підключення), не маючи можливості взагалі нічого ділити між ними (адже якщо ми навіть зможемо поділитися файлом, ну це спільний ресурс, порушення основної передумови ні про що не поділяється).
Кожен цікавий випадок передбачає певне поєднання незалежних ресурсів та спільних ресурсів. Кожен досить сучасний комп'ютер (і багато, що зовсім не сучасний) має принаймні деяку здатність одночасно виконувати принаймні кілька незалежних операцій, і майже що-небудь більш досконале, ніж MS-DOS, скористалося цим принаймні. деякий ступінь.
Приємного, чистого поділу між "паралельним" та "паралельним", яке люблять малювати, просто не існує, і майже ніколи його немає. Те, що люблять класифікувати як "одночасне", як правило, все ще включає принаймні один і часто більше різних видів паралельного виконання. Те, що вони люблять класифікувати як "паралельне", часто включає обмін ресурсами та (наприклад) один процес, що блокує виконання іншого при використанні ресурсу, який спільний між ними.
Люди, які намагаються зробити чітке розмежування між "паралельним" і "паралельним", живуть у фантазії комп'ютерів, які ніколи насправді не існували.