Що регулює "швидкість" мови програмування?
Немає такого поняття, як "швидкість" мови програмування. Існує лише швидкість певної програми, записана певним прогамером, виконана певною версією певної реалізації певного двигуна виконання, що працює в певному середовищі.
У роботі одного і того ж коду, написаного однією і тією ж мовою на одній машині з використанням різних реалізацій, можуть бути величезні відмінності. Або навіть використовувати різні версії однієї і тієї ж реалізації. Наприклад, запуск того самого еталону ECMAScript на цій же машині з використанням версії SpiderMonkey від 10 років тому проти версії цього року, ймовірно, призведе до підвищення продуктивності в будь-якому місці між 2 × –5 ×, можливо, навіть на 10 ×. Чи означає це тоді, що ECMAScript на 2 × швидше, ніж ECMAScript, тому що запуск нової програми на одній машині на 2 × швидше з новою реалізацією? Це не має сенсу.
Це щось пов’язане з управлінням пам’яттю?
Не зовсім.
Чому це відбувається?
Ресурси. Гроші. Microsoft, ймовірно, працює більше людей, які готують каву для своїх програмістів-компіляторів, ніж у всій спільноті PHP, Ruby та Python. У спільній роботі працюють люди, що працюють над їх віртуальними машинами.
Для більш-менш будь-якої функції мови програмування, яка певним чином впливає на продуктивність, також існує рішення. Наприклад, C (я використовую C тут як резервне використання для класу подібних мов, деякі з яких існували навіть до C) не є безпечним для пам'яті, тому кілька програм C, що працюють одночасно, можуть топтатись пам'ять один одного. Отже, ми вигадуємо віртуальну пам'ять і змушуємо всі програми C проходити через шар непрямості, щоб вони могли зробити вигляд, що вони єдині, що працюють на машині. Однак це повільно, і так, ми вигадуємо MMU і впроваджуємо віртуальну пам'ять в апаратне забезпечення, щоб прискорити її.
Але! Мови, безпечні для пам’яті, не потребують усього цього! Наявність віртуальної пам'яті не допомагає їм ні на один біт. Насправді, це ще гірше: не тільки віртуальна пам'ять не допомагає безпечним для пам’яті мовам, віртуальна пам’ять, навіть якщо вона реалізована в апаратному забезпеченні, все ще впливає на продуктивність. Це може бути особливо шкідливим для роботи сміттєзбірників (саме для цього використовується значна кількість реалізацій мов, безпечних для пам’яті).
Інший приклад: сучасні центральні процесори загального призначення використовують складні прийоми, щоб зменшити частоту пропусків кешу. Дуже багато цих хитрощів є спробою передбачити, який код буде виконуватися і яка пам'ять буде потрібна в майбутньому. Однак для мов з високим ступенем поліморфізму виконання (наприклад, мови OO) передбачити ці схеми доступу дуже важко.
Але є й інший спосіб: загальна вартість пропусків кешу - це кількість пропусків кешу, помножена на вартість окремої пропуски кешу. Основні процесори намагаються зменшити кількість пропусків, але що робити, якщо ви могли зменшити вартість окремих пропусків?
Процесор Azul Vega-3 був спеціально розроблений для роботи віртуалізованих JVM, і він мав дуже потужний MMU з деякими спеціалізованими інструкціями щодо сприяння збору сміття та виявлення втечі (динамічний еквівалент статичному аналізу втечі), потужними контролерами пам'яті та всією системою все-таки вдасться досягти прогресу з понад 20000 непогашених пропусків кешу в польоті. На жаль, як і у більшості мовних процесорів, його дизайн був просто витрачений та вимушений «гігантами» Intel, AMD, IBM тощо.
Архітектура процесора - це лише один приклад, який впливає на те, наскільки легко чи наскільки важко мати високопродуктивну реалізацію мови. Таку мову, як C, C ++, D, Rust, яка добре підходить для сучасної основної моделі програмування процесора, буде простіше зробити швидше, ніж мова, яка повинна "боротися" і обійти процесор, як Java, ECMAScript, Python, Ruby , PHP.
Дійсно, це все питання грошей. Якщо ви витрачаєте рівні кошти на розробку високопродуктивного алгоритму в ECMAScript, високопродуктивну реалізацію ECMAScript, високопродуктивну операційну систему, розроблену для ECMAScript, високопродуктивний процесор, розроблений для ECMAScript, який був витрачений протягом останнього Десятиліття, щоб швидко подібні мови проходили швидко, ви, швидше за все, побачите рівну продуктивність. Просто в цей час було витрачено набагато більше грошей, роблячи C-подібні мови швидшими, ніж швидкісні мови, подібні до ECMAScript, і припущення, що схожі на С мови, перекладаються на весь стек від MMU та процесорів до операційних систем і системи віртуальної пам'яті до бібліотек і фреймворків.
Особисто мені найбільше знайомий Рубі (який, як правило, вважається "повільною мовою"), тому я наведу два приклади: Hash
клас (одна з центральних структур даних у Ruby, словник ключових значень) у Rubinius Реалізація Ruby написана на 100% чистому Ruby, і вона має приблизно таку ж продуктивність, як іHash
клас у YARV (найбільш широко використовувана реалізація), написаний на C. І є бібліотека маніпуляцій із зображеннями, написана як розширення C для YARV, яка також має (повільну) чисту Ruby "резервну версію" для реалізацій, які не 't підтримка C, яка використовує тонну високодинамічних та рефлексивних трюків Ruby; експериментальна гілка JRuby, використовуючи структуру інтерпретатора TST Truffle та рамку компіляції Graal JIT від Oracle Labs, може виконати цю чисту Ruby "резервну версію" так само швидко, як YARV може виконати оригінальну високооптимізовану версію C. Це просто (ну, що завгодно) досягається деякими дійсно розумними людьми, які роблять дійсно розумні речі з динамічними оптимізаціями виконання, компіляцією JIT та частковою оцінкою.