Чому нам доводиться торгувати абстракцією для швидкості?


11

Чому мови високого рівня, схоже, ніколи не досягають мов нижчого рівня за швидкістю? Прикладами мов високого рівня можуть бути Python, Haskell та Java. Мови низького рівня були б складнішими для визначення, але скажімо C. Порівняння можна знайти по всьому Інтернету 1, і всі вони згодні з тим, що C набагато швидше, іноді в 10 чи більше разів.1

Що є причиною такої великої різниці в продуктивності, і чому мови високого рівня не можуть наздогнати?

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


Приклади:1


5
"І всі вони згодні, що С набагато швидше" - це, звичайно, неправильно.
Рафаель

2
У всякому разі, я думаю, що пост найкраще відповідає моєю відповіддю на аналогічно непродумане питання ; дублікат?
Рафаель

2
Дивіться також тут і тут . Слідкуйте за непотрібними відповідями.
Рафаель

Доречно: stackoverflow.com/questions/6964392/… Міф про те, що про "мови високого рівня" про повільність є досить сумно.
xji

Я погоджуюся з іншими, що абстракція! = Повільність, але я боюся, що існує набагато більша проблема, про яку не знають вчителі інформатики (я була такою). Тобто, для реальних програм (1000 рядків коду і вище) існує безліч способів їх виконання, і вони можуть відрізнятися за продуктивністю на порядки. Думаючи про big-O, повністю пропускає суть. Перевірте тут .
Майк Данлаве

Відповіді:


19

Розвінчання деяких міфів

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

  2. Код C, як правило, швидше, тому що люди, яким потрібен кожен дюйм продуктивності, використовують C. Статистика, що C швидше "в 10 разів", може бути неточною, тому що, можливо, люди, які використовують Python, просто так не хвилюються про швидкість і не написав оптимальний код Python. Це ми бачимо, зокрема, з такими мовами, як Haskell. Якщо ви дуже стараєтесь , ви можете написати Haskell, який виступає нарівні з C. Але більшості людей ця робота не потрібна, тому у нас є купа хибних порівнянь.

  3. Іноді C швидко робить небезпеку, а не абстрагування. Відсутність меж масиву та перевірки нульових покажчиків економлять час і стали причиною численних дір у безпеці протягом багатьох років.

  4. Мови не швидкі, реалізація - швидка. Багато абстрактних мов починаються повільно, оскільки швидкість не є їхньою метою, але стає швидшою, оскільки додається все більше оптимізацій.

Можливо, компроміс абстракції проти швидкості, можливо, неточний. Я б запропонував краще порівняння:

Простота, швидкість, абстракція: виберіть два.

Якщо ми використовуємо однакові алгоритми на різних мовах, питання швидкості зводиться до проблеми "Скільки потрібно нам робити під час виконання, щоб зробити цю роботу?"

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

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

Нарешті, більшість абстрактних мов мають накладні витрати зі збирання сміття. Більшість програмістів погодилися, що відстежувати тривалість життя динамічно розподіленої пам'яті - це біль, і вони, швидше, вимагають, щоб колектор сміття робив це для них під час роботи. Це вимагає часу, щоб програму C не потрібно витрачати на GC.

Анотація не означає повільний

Однак є мови, які є і абстрактними, і швидкими. Найбільш домінуючою в цю епоху є Руст. Запровадивши перевірку позик та складну систему типів, вона дозволяє отримати абстрактний код і використовує інформацію про час збирання, щоб зменшити обсяг роботи, яку нам потрібно виконати під час виконання (тобто, збирання сміття).

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


2
"C код має тенденцію бути швидшим, тому що люди, яким потрібен кожен дюйм продуктивності, використовують C" - саме так. Я хочу побачити орієнтири форми "середній час роботи коду, написаний студентами X-курсу / професіоналами з Y-річним досвідом роботи в Z". Я очікую, що відповідь на C зазвичай є "неправильним кодом" для малих X та Y. Було б дуже цікаво побачити, наскільки більше досвіду / досвіду вам потрібно використовувати потенціал для виконання обіцянок C.
Рафаель

Haskell - це справді виняток, який підтверджує правило. ;) Я думаю, що C ++ знайшов досить розумну серединку щодо GC своїми розумними покажчиками, доки ви не вкладете загальні покажчики і будете так швидко, як C.
Kaveh

0

ось кілька ключових ідей щодо цього.

  • просте порівняння / тематичне дослідження, щоб зробити абстракцію Wrt vs швидкістю java vs c ++. java була покликана абстрагувати деякі аспекти нижчого рівня c ++, такі як управління пам'яттю. У перші дні (приблизно винахід мови в середині 1990-х років) виявлення сміття Java було не дуже швидким, але після кількох десятиліть досліджень сміттєзбірники надзвичайно тонко / швидко / оптимізовано, тому сміттєзбірники значною мірою усуваються як продуктивність стік на Java. наприклад, дивіться навіть цей заголовок 1998 року: Тести на ефективність показують Java так само швидко, як C ++ / javaworld

  • Мови програмування та їх тривала еволюція мають властиву "пірамідальну / ієрархічну структуру" як своєрідну трансцендентну модель дизайну. вгорі піраміди - це те, що керує іншими нижніми ділянками піраміди. Іншими словами, будівельні блоки складаються з будівельних блоків. це можна побачити і в структурі API. у цьому сенсі більша абстракція завжди призводить до появи нового компонента у верхній частині піраміди, що контролює інші компоненти. так що в певному сенсі це не стільки, щоб усі мови були рівні між собою, але і що мови вимагають підпрограм інших мов. наприклад, багато мов сценаріїв (python / ruby) часто викликають бібліотеки C або C ++, типовим прикладом цього є числові або матричні підпрограми. тому існують мови вищого та нижчого рівнів, а мови високого рівня називають мовами нижчого рівня так би мовити. у цьому сенсі вимірювання відносної швидкості насправді не порівнянне.

  • можна сказати, що нові мови постійно вигадуються, щоб спробувати оптимізувати компроміс абстракції / швидкості, тобто його ключову дизайнерську мету. можливо, це не стільки в тому, що більша абстракція завжди приносить в жертву швидкість, але щоб завжди досягати кращого балансу за допомогою нових конструкцій. наприклад, Google Go багато в чому був спеціально оптимізований з урахуванням компромісу, щоб бути одночасно високим і ефективним. див., наприклад, Google Go: Чому мова програмування Google може конкурувати з Java в корпоративному / технологічному світі


0

Мені подобається думати про продуктивність - "там, де гума зустрічає дорогу". Комп'ютер виконує вказівки, а не абстракції.

Що я хочу побачити, це таке: чи кожна інструкція, яка виконується, "заробляє на своєму", роблячи істотний внесок у кінцевий результат? Як надто простий приклад, розгляньте пошук запису в таблиці з 1024 записів. Це 10-бітова проблема, оскільки програма повинна "вивчити" 10 біт, перш ніж вона дізнається відповідь. Якщо алгоритм є двійковим пошуком, то кожна ітерація вносить 1 біт інформації, оскільки вона зменшує невизначеність в 2 рази. Тому потрібно 10 ітерацій, по одному на кожен біт.

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

Гаразд, тому якщо компілятор може дозволити користувачеві перекладати хороші інструкції таким чином, що вважається "абстрактним", це добре.


0

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

Приклад потенційної вигоди від цього аспекту "байдуже" - це макет даних. У C (низька абстракція) компілятор більш обмежений в оптимізації верстки даних. Навіть якщо компілятор міг би помітити (наприклад, за допомогою профільної інформації), що оптимізація гарячого / холодного або неправдивого обміну-уникнення була б корисною, як правило, не можна застосовувати таку. (Існує деяка свобода в заданні "як би", тобто трактування специфікації більш абстрактно, але отримання всіх потенційних побічних ефектів додає тягар компілятору.)

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

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

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

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

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

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

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

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

Ще одним заплутаним фактором у таких порівняннях є дещо ортогональна різниця між достроковою компіляцією та складанням, що склалася за часом. У той час як компіляція, що працює вчасно, може легше використовувати інформацію профілю (не покладаючись на програміста, щоб забезпечити запуск профілю) та специфічну для системи оптимізацію (дострокова компіляція може орієнтуватися на більшу сумісність), накладні витрати на агресивну оптимізацію обліковуються як частина виконання. Результати JIT можна кешувати, зменшуючи накладні витрати для часто використовуваного коду. (Альтернатива бінарної повторної оптимізації може забезпечити деякі переваги компіляції JIT, але традиційні формати бінарного розподілу скидають більшість інформації вихідного коду, що потенційно змушує систему намагатися розпізнати наміри від конкретної реалізації.)

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

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

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.