Ви надто спростили твердження Гідо, формулюючи своє запитання. Проблема полягає не в написанні компілятора для динамічно набраної мови. Проблема полягає в тому, щоб записати той, який (критерії 1) завжди правильний, (критерії 2) зберігає динамічне введення тексту, а (критерії 3) помітно швидше для значної кількості коду.
Реалізувати 90% (невдалі критерії 1) Python легко і бути послідовно швидким. Так само легко створити більш швидкий варіант Python зі статичним набором тексту (невдалі критерії 2). 100% реалізація також проста (якщо реалізувати складну мову легко), але поки що кожен простий спосіб її здійснення виявляється відносно повільним (невдалі критерії 3).
Реалізація інтерпретатора плюс правильний JIT , реалізація всієї мови, і швидше для деякого коду виявляється здійсненною, хоча значно складніше (пор. PyPy), і тільки так, якщо ви автоматизуєте створення компілятора JIT (Psyco не обійшлося без цього , але був дуже обмежений у тому, який код може пришвидшити). Але зауважте, що це явно не виходить за межі, оскільки ми говоримо про статичне(він же заздалегідь) компіляторів. Я згадую це лише для пояснення, чому його підхід не працює для статичних компіляторів (або, принаймні, немає існуючого контрприкладу): він спочатку повинен інтерпретувати та спостерігати за програмою, а потім генерувати код для конкретної ітерації циклу (або іншого лінійного коду path), а потім оптимізуйте пекло, виходячи з припущень, що стосуються лише конкретної ітерації (або, принаймні, не для всіх можливих ітерацій). Очікується, що багато пізніших виконання цього коду також відповідатимуть очікуванням і, таким чином, виграють від оптимізацій. Для забезпечення коректності додаються деякі (відносно дешеві) чеки. Щоб зробити все це, вам потрібно уявити, для чого слід спеціалізуватися, і повільну, але загальну реалізацію, до якої слід повернутися. У компіляторів AOT немає жодного. Вони не можуть спеціалізуватися на всіхна основі коду, який вони не можуть бачити (наприклад, динамічно завантажений код), а спеціалізація недбало означає генерувати більше коду, що має ряд проблем (використання icache, використання двійкового розміру, час компіляції, додаткові гілки).
Реалізація компілятора AOT, який правильно реалізує всю мову, також відносно проста: Створіть код, який запускає час виконання, щоб робити те, що зробить інтерпретатор, коли він подається з цим кодом. Нуїтка (в основному) робить це. Однак це не приносить великої користі для продуктивності (невдалі критерії 3), оскільки вам все одно доведеться виконувати так само непотрібну роботу, як інтерпретатор, за винятком того, щоб відправити байт-код до блоку коду С, який виконує те, що ви склали. Але це лише досить невелика вартість - достатньо значна, щоб її варто було оптимізувати в існуючому перекладачі, але недостатньо істотна, щоб виправдати цілком нову реалізацію власними проблемами.
Що потрібно для виконання всіх трьох критеріїв? Ми поняття не маємо. Існують деякі схеми статичного аналізу, які можуть отримати певну інформацію про конкретні типи, контрольний потік тощо з програм Python. Ті, які видають точні дані за межі одного базового блоку, надзвичайно повільні і потребують перегляду всієї програми або принаймні більшості її. Тим не менш, ви не можете багато зробити з цією інформацією, крім можливо оптимізувати кілька операцій над вбудованими типами.
Чому це? Якщо сказати прямо, компілятор або видаляє можливість виконання коду Python, завантаженого під час виконання (невдалі критерії 1), або не робить жодних припущень, які взагалі можуть бути визнані недійсними будь-яким кодом Python. На жаль, це включає в себе майже всі , що корисно для оптимізації програм: Globals включаючи функцію може бути перевизначені, класи можуть бути мутують або повністю замінено, модулі можуть бути змінені довільно занадто, імпорт можуть захопити кілька способів, і т.д. Одна рядки передається eval
, exec
, __import__
або численні інші функції, можуть виконувати будь-що з цього. Насправді, це означає, що майже не можна застосовувати великих оптимізацій, що дасть невелику користь від продуктивності (невдалі критерії 3). Повернення до вищевказаного абзацу.