Модель виконання

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку

Мова програмування складається з граматики/синтаксису та моделі виконання. Модель виконання визначає поведінку елементів мови. Застосувавши модель виконання, можна отримати поведінку програми, написаної на цій мові програмування. Наприклад, коли програміст «читає» код, у своєму розумі він переглядає, що робить кожен рядок коду. По суті, він імітує поведінку всередині свого розуму. Те, що робить програміст, застосування моделі виконання до коду, що призводить до поведінки коду.

Кожна мова програмування має модель виконання, яка визначає спосіб, у який планується виконання одиниць роботи (позначених синтаксисом програми). Детальні приклади специфікації моделей виконання кількох популярних мов включають моделі Python,[1] модель виконання мови програмування Unified Parallel C (UPC),[2] обговорення різних класів моделей виконання, таких як імперативна проти функціональних мов[3] і статтю, в якій обговорюються моделі виконання для вбудованих мов реального часу.[4]

Деталі моделі виконання[ред. | ред. код]

Операційна семантика є одним із методів визначення моделі виконання мови. Спостережувана поведінка запущеної програми має відповідати поведінці, отриманій з операційної семантики (яка визначає модель виконання мови).

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

Щоб проілюструвати це, розглянемо мову програмування C, як описано в книзі Кернігана та Річі.[5] C має поняття, яке називається оператором. Специфікація мови визначає інструкцію як фрагмент синтаксису, який закінчується символом ;. Далі у специфікації мови сказано:

виконання програми виконується один оператор за іншим, послідовно.

Ці слова є частиною моделі виконання C і говорять нам, що оператори є неподільними одиницями роботи; вони виконуються в тому ж порядку, що й їхня синтаксична поява в коді (за винятком випадків, коли оператор керування, такий як IF або FOR, змінює порядок). Модель програмування встановила обмеження на порядок виконання одиниць роботи.

Мова C фактично має додатковий рівень у своїй моделі виконання, який є порядком пріоритету. Порядок пріоритету встановлює правила для порядку операцій в одному операторі. Порядок пріоритету можна розглядати як встановлення обмежень на виконання одиниць роботи, які знаходяться в одному операторі. Тому, ;, IF і WHILE охоплюють обмеження на порядок операторів, тоді як порядок пріоритету охоплює обмеження на роботу в операторі. Отже, ці частини специфікації мови C також є частиною моделі виконання мови C.

Моделі виконання також можуть бути незалежно від мов програмування, прикладами яких можуть бути бібліотека POSIX Threads і модель програмування Map-Reduce від Hadoop. Реалізація моделі виконання може відбуватися через компілятор або інтерпретатор і часто містить систему середовища виконання.

Реалізація моделі виконання контролює порядок, у якому відбувається робота під час виконання. Цей порядок може бути обраним заздалегідь, або бути визначеним динамічно під час виконання. Більшість моделей виконання допускають різні ступені обох. Наприклад, мова C фіксує порядок роботи в операторі та порядок усіх операторів, крім тих, які включають оператор IF або форму оператора циклу. Отже, більшу частину порядку виконання можна вибрати статично, до початку виконання, але невелику частину потрібно вибрати динамічно, під час виконання.

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

Однак інтерпретатор також може бути створений для будь-якої мови, у цьому випадку всі рішення щодо порядку виконання є динамічними. Інтерпретатор можна розглядати як частину перекладача та частину реалізації моделі виконання.

Модель виконання мови асемблера проти реалізації мікроархітектурами[ред. | ред. код]

Мови асемблера також мають моделі виконання, як і будь-яка інша мова. Така модель виконання реалізована мікроархітектурою ЦП. Наприклад, як 5-ступінчастий конвеєр у порядку, так і великий центральний процесор, який не працює, реалізують однакову модель виконання мови асемблера. Модель виконання — це визначення поведінки, тому всі реалізації, будь то впорядковані чи позачергові, чи інтерпретовані, чи JIT тощо, мають давати абсолютно однаковий результат, і цей результат визначається моделлю виконання.

Моделі паралельного виконання[ред. | ред. код]

У сучасну епоху паралельне програмування стає все більш важливою темою. Моделі паралельного виконання, як правило, складні, оскільки включають кілька часових шкал. Моделі паралельного виконання обов'язково включають поведінку конструкцій синхронізації. Конструкція синхронізації має ефект встановлення порядку між діями на одній часовій шкалі відносно дій на іншій часовій шкалі.

Наприклад, загальною конструкцією синхронізації є блокування. Розглянемо одну шкалу часу. Часова шкала має точку, в якій виконується конструкція синхронізації «отримати право власності на блокування». У потоках Posix це буде pthread_mutex_lock(&myMutex). У Java це буде lock.lock(). В обох випадках шкала часу називається потоком. Моделі виконання C і Java є послідовними, і вони стверджують, що на часовій шкалі є дії, які відбуваються перед викликом «отримати право власності на блокування», і дії, які відбуваються після виклику. Так само існує операція «відмовитися від власності на замок». У C це буде pthread_mutex_unlock(&myMutex). У Java це буде lock.unlock(). Знову ж таки, моделі виконання C і Java визначають, що одна група операторів виконується до того, як право власності на блокування відмовляється, а інша група операторів виконується після того, як право власності на блокування відмовляється.

Тепер розглянемо випадок двох часових шкал, також відомих як два потоки. Один потік, назвемо його потоком A, виконує деякі оператори, назвемо їх операторами A-pre-gain-lock. Потім потік A виконує «отримати право власності на блокування», потім він виконує оператори A-post-gain-lock, які надходять після отримання права власності на блокування А. Нарешті, потік A виконує "відмову від права власності на блокування". Потім потік A виконує оператори A-post-giveup-lock.

Другий потік, назвемо його потік B, виконує деякі оператори, назвемо їх операторами B-pre-lock. Потім потік B виконує «отримати право власності на блокування», потім потік B виконує оператори B-post-lock, які надходять після того, як B отримує право власності на блокування.

Тепер ми можемо сказати, що модель паралельного виконання конструкції синхронізації «отримати право власності на блокування» та «відмовитися від права власності на блокування». Модель виконання така:

«У випадку, коли право власності на блокування переходить від потоку A до потоку B, оператори A-post-gain-lock передують операторам B-post-gain-lock».


Складність виникає через те, що модель виконання не має жодних засобів для виконання «відмовитися від володіння блокуванням», щоб мати будь-який вплив на те, яке виконання «отримання володіння блокуванням» в іншій часовій шкалі (потоці) слідує. Дуже часто лише певні передачі дають дійсні результати. Таким чином, програміст мусить продумати всі можливі комбінації, коли один потік відмовляється від блокування, а інший потік отримує його наступним, і переконатися, що код допускає лише дійсні комбінації.

Єдиним ефектом є те, що оператори A-post-gain-lock передують операторам B-post-gain-lock. Ніякого іншого ефекту не відбувається, і на інше відносне впорядкування не можна покладатися. Зокрема, A-post-give-up-lock і B-post-gain-lock не мають визначеного відносного порядку, що дивує багатьох людей. Але потік A міг бути заміненим після відмови від права власності, тому оператори A-post-give-up-lock можуть виникнути через довгий час після завершення багатьох операторів B-post-gain-lock. Це одна з можливостей, про яку слід думати під час проектування блокувань, і вона ілюструє, чому багатопотокове програмування складне.

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

Дивіться також[ред. | ред. код]

Список літератури[ред. | ред. код]

  1. Python Documentation: Execution Model (англ.).
  2. UPC Language Features (англ.).
  3. Cardoso, J.M.P.; Diniz, P.C. (2011). Programming Languages and Execution Models (англ.). Springer US. ISBN 9780387096711.
  4. PELLIZZONI, R.; BETTI, E.; BAK, S.; YAO, G.; CRISWELL, J.; CACCAMO, M.; KEGLEY, R (2011). A Predictable Execution Model for COTS-based Embedded Systems (PDF). Real-Time and Embedded Technology and Applications Symposium (англ.). IEEE. Архів оригіналу (PDF) за 12 серпня 2017. Процитовано 14 липня 2022.
  5. Керніган, Браян; Денніс Рітчі (Лютий 1978). The C Programming Language (англ.) (вид. 1). Енглвуд-Кліффс (Нью-Джерсі): Prentice Hall. ISBN 0-13-110163-3.