Чи є у функціональних мов програмування більше можливостей зробити оптимізацію часу компіляції?


10

Я читав книгу «Функціональне програмування для реального світу». Він розпочався з порівняння імперативних та функціональних мов програмування. І в ньому зазначалося, чим «значення» та «вирази» у функціональному програмуванні відрізняються від «змінних» та «функцій» імперативного програмування. З обговорення я наче розробив ідею, що -

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

Це правда?

Відповіді:


16

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

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

Крім того, все, що не потрібно обчислювально, можна повністю оптимізувати з програми.


1
+1: Безкоштовне програмування з побічними ефектами оптимізувати дуже, дуже просто.
S.Lott

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

2
@mathepic: чергове використання чистоти -> лінь і відсутність обчислень. Якщо ви можете довести, що значення не потрібне, то зніміть обчислення цілком. Якщо це може знадобитися, використовуйте ледачий шматок. Якщо ви знаєте, що це знадобиться, обчисліть його прямо вперед (щоб уникнути лінивих накладних витрат). Чистота (просто) дивовижна :)
Матьє М.

@Matthieu хороший момент
альтернатива

1
@Masse Навіть монада IO чиста. Справа лише в тому, що результат «запуску» МО монади не є.
альтернатива

7

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

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

наприклад, подання Haskell для сумнозвісної гри на комп'ютерній мові Benchmark (як це погано, але це не так, як є - на даний момент - щось набагато краще там) створюють враження, що на це витрачено значну кількість часу ручні оптимізації, які стикаються з твердженням про "можливі оптимізації компілятора завдяки insert some property about FP languages here", виглядають так, що оптимізації є (наразі принаймні) більш теоретичними можливостями, ніж відповідною реальністю.

Я був би радий, хоча в цьому питанні я виявився неправильним.


1
Причина ручних оптимізацій в Haskell більше пов'язана з тим, що деякі "прямі" операції дещо забирають багато часу (з точки зору складності) в Haskell. Наприклад, скажіть, що ви хочете отримати останній елемент списку. У Java це досить проста операція; в Haskell, наївний підхід вимагає, щоб ви пройшли весь список, поки ви не закінчитесь (через ледачий характер списків), зробивши це O (n) операцією. Ось (частково), де надходять ручні оптимізації.
mipadi

2
Я впевнений, що існують вагомі причини для оптимізації ручного прокату Haskell, але вони, необхідні для «простих» операцій, створюють враження, що більші можливості для оптимізації коду (наразі) актуальні лише в теорії.
Олександр Баттісті

6
Більше різниця між оптимізацією алгоритмів та оптимізацією згенерованого коду. Візьмемо приклад С: якщо я пишу алгоритм пошуку, я можу написати наївний алгоритм, який просто проходить список, або я можу використовувати двійковий пошук. В обох випадках компілятор оптимізує мій код, але це не перетворить алгоритм наївного пошуку в двійковий пошук. Багато ручних оптимізацій у коді Haskell пов'язані більше з оптимізацією самих алгоритмів, а не з оптимізацією створеного коду.
mipadi

2
@mipadi, але здається, що пряма версія алгоритму Haskell не настільки хороша, як пряма версія алгоритмів інших мов. (Я підозрюю через невідповідність між функціональною моделлю та архітектурою комп’ютера) Навіть якщо її добре в створенні коду, його недостатньо для подолання цієї проблеми.
Вінстон Еверт

>> погано, як це може бути << - Чи знаєте ви, погано це чи ні? Що ви навіть маєте на увазі, підказавши, що це "погано"? Будь ласка, будьте конкретні.
igouy

2

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

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

Зараз є два застереження:

  • Спільнота мов програмування протягом останніх п'ятдесяти років витратила багато зусиль на розробку розумних способів цього аналізу. Існують відомі хитрощі, такі як забарвлення реєстру тощо, щоб зробити більш прості випадки більшу частину часу; але це робить великими, повільними компіляторами та постійним компромісом складності компіляції для якості отриманого коду
  • У той же час більшість функціональних мов програмування також не є суто функціональними; багато речей, які насправді потрібно робити програмам, як-от відповідь на введення-виведення, за своєю суттю нефункціональні, тому жоден компілятор не може бути абсолютно вільним від цих хитрощів, і жодна мова не уникає їх цілком - навіть Haskell, що теж трохи чисто на мій смак (Ваша пробіг може змінюватись) може лише контролювати та відключати нефункціональні частини вашого коду, а не уникати їх взагалі.

Але, щоб відповісти на загальне запитання, так, функціональна парадигма дає компілятору багато свободи для оптимізації, якої вона не має в обов'язкових умовах.


Все в Haskell є функціональним, це лише те, що ваша mainє функцією перетворення стану, а не тим, що використовує сам стан.
альтернатива

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

@jimwise Референсна прозорість - це дуже велика різниця.
альтернатива

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

1
@mathepic Так, звичайно, ви можете концептуально розглядати випадковість або введення користувача (або затримку мережі або завантаження системи) як нескінченний потік або функцію від стану до стану, але це не погляд, який піддається корисним міркуванням про поведінку програми шляхом ви або ваш упорядник - Боб Харпер добре висвітлює цю основу в недавньому дописі у своєму блозі про новий функціонально-програмований перший навчальний план CS.
jimwise
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.