Подтвердить что ты не робот

Поддерживает ли функциональное программирование лучшую оптимизацию компилятора во время выполнения?

ПРИМЕЧАНИЕ. Уже сделана это Wiki. Меня не волнует, что этот вопрос отмечен как, пока есть хорошая дискуссия.

Я слышал, что, поскольку в чистых функциональных программах нет побочных эффектов, а значения не мутируют, это упрощает компилятор для большей оптимизации времени выполнения. Насколько это верно?

Если это правда, моя следующая забота заключается в том, какова потеря в свободе, для которой мы торгуем этим? Я имею в виду, что в таких языках, как С++/C, разработчик полностью контролирует и может настраивать множество вещей. Если мы отдадим эту работу компилятору, мы потеряем эту возможность. Яркая сторона этого заключается в том, что даже не-эксперт-программист может писать хороший код. Кроме того, в эти дни с таким количеством слоев кеша в архитектуре машины может быть даже эксперт, который не может действительно сделать что-либо стоящее. Итак, делегирование этой работы компилятору, который знает больше о базовой архитектуре, чем программист, является хорошей идеей.

Каковы ваши предложения?

4b9b3361

Ответ 1

Вы видели это сообщение "good math, bad math" ?

Я программирую на C, сборке и OCaml. Иногда я смотрю на сборку, сгенерированную компилятором C. Он неэффективен. Часто я замечаю, что глобальная переменная повторно загружается снова и снова, когда она очищается (кому-то известно о логике программы), что она не изменяется во время выполнения функции, но поскольку функция манипулирует указателями, компилятор не может предположить, что переменная не затронута. Один простой способ исправить это, когда вы заметили, что это копирование содержимого глобального в локальное в начале функции, таким образом вы сохраняете переносимость C, но получаете производительность, подобную сборке...

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

Ответ 2

Просто, чтобы быть ясным, только потому, что компилятор для функционального языка может оптимизировать лучше, не означает, что он действительно делает. Любая безумная оптимизация по определению приводит к неожиданному поведению во время выполнения, что почти наверняка приводит к ошибкам. Поэтому, чтобы поставить большое заблуждение для отдыха: в теории программы Haskell могут быть распараллелены "бесплатно" компилятором, но на самом деле они не являются и не должны быть.

Я думаю, что дни оптимизации компилятора были королем. Когда clockspeed был основным узким местом для оптимизации производительности, например, для разворота цикла, были необходимы. Однако для подавляющего большинства приложений проблема заключается не в сырой скорости процессора, а в других факторах, таких как пропускная способность сети, диск IO и/или скорость алгоритма, который вы используете.

Насколько я знаю, оптимизация компилятора для переопределения кода для использования параллельного шаблона проектирования, например, отсутствует. Другими словами, если вы используете Bubble Sort с каждой оптимизацией компилятора, о которой вы можете думать, и я использую Quick Sort. В большинстве ситуаций умные деньги находятся в реализации Quick Sort.

Существует большое значение для функционального программирования, но "потому что они работают быстрее" не должно рассматриваться.

Ответ 3

нет побочных эффектов и значений не мутировать, это облегчает компилятор сделает больше времени выполнения Оптимизация

В некотором смысле, да - пока вы не поймете, что побочные эффекты и изменчивые значения сами являются оптимизациями.

Ответ 4

Посмотрите сравнение Haskell vs C здесь:

http://www.haskell.org/haskellwiki/Introduction#Quicksort_in_Haskell

Вы заметите, что верификация Haskell намного короче. Тем не менее, страница продолжает утверждать, что, хотя версия c, возможно, более сложна, она также сортируется на месте, в то время как версия Haskell должна выделять гораздо больше памяти для работы.

Итак, по определению, если вы хотите получить предельную тонкую настройку производительности, алгоритм c является лучшим подходом.

Ответ 5

Я думаю, вы должны говорить о языковых моделях, а не о оптимизации компилятора. императивные процедурные/функциональные имеют свою собственную область, где они сияют. Я приведу вам два примера:

Императив - процедурный

Эрланг (функциональный язык). Принял подход к совместному состоянию со своей встроенной в базу данных Mnesia - по своей сути нефункционально, поскольку транзакция обновления блокирует ресурс базы данных, получает значение, изменяет его и записывает обратно. Они сделали это для производительности, потому что они признали, что скорость важна в этой области, и если они этого не сделали, Эрланг был бы бесполезен для проблем, которые они пытались решить (можете ли вы представить, как писать весь файл базы данных каждый раз, когда вы делаете измените?) = D.

Чистый функциональный

Функциональная и не функциональная с точки зрения производительности, допускаемой моделью, является смешной предметной областью. Принимая функциональную точку зрения; Erlang может насытить ядро, на котором работает компьютер, когда проблемы по своей сути ориентированы на concurrency. Некоторые тесты показали, что веб-сервер YAWS обрабатывает 100 тыс. Соединений, где Apache упал на 4k: D Ejabberd также может обрабатывать MUCH больше нагрузки, чем традиционные переключатели сообщений, Я пытаюсь сказать, что чистый функциональный язык может иметь механизм выполнения, который может массивно распараллеливать приложение на большом количестве ядер. Вы не можете легко сделать это с императивным процедурным кодом.

Различные модели подходят для различных задач. Я лично думаю, что вы можете оптимизировать процедурный код намного лучше, чем функциональный код, функциональный код по-прежнему выполняется как процедурный код, и он интерпретируется: PI изучил теорию компилятора, и я честно не могу думать о продуманных кодовых преобразованиях, которые я бы применил до функционального кода, прежде чем я его выполнил. У вас есть хвостовая рекурсия и ленивая оценка, но, безусловно, это функции модели программирования.

Ответ 6

Вот пример из Clojure - итерация по списку вещей с использованием некоторой функции.

(map some-fn big-list)

Замечательно, что вы можете распараллелить его (несколько ядер, а не несколько машин) одним символом:

(pmap some-fn big-list)

Механизм языка/компилятора, который делает работу pmap, невозможен без чистых функций и неизменных структур данных.

Это все еще экспериментальная функция. Но функциональное программирование без побочных эффектов делает многоядерное выполнение намного проще.

Ответ 7

когда дело доходит до C или С++, существует много оптимизаций компилятора, и компилятор может решить свергнуть любую из маленьких маленьких идей, так как он может определить намного лучшую оптимизацию для использования в определенном месте ( таких как inlining) и учитывает вещи как конвейеры команд и т.д. Архитектура оборудования стала слишком сложной для людей напрямую программировать аппаратное обеспечение. в настоящее время даже простые компиляторы используют значительную оптимизацию. использование фанковых трюков менее читаемо и сложнее для компилятора для оптимизации, поскольку ручная оптимизация уменьшает семантику. поэтому, даже если вы пишете C или С++, ваш выбор - либо застрелить себя в ноге, либо позволить компилятору сделать оптимизацию для него. Это то, что большинство из 9 513 227 строк кода в GCC около.

Я думаю, очень важно, чтобы программное обеспечение было поддающимся проверке, надежным, удобным, гибким и многоразовым/расширяемым. чем более выразительным и лаконичным является язык, тем легче разработчикам соответствовать этим критериям.

есть один последний, но важный критерий для программного обеспечения: эффективность. важный вопрос: как вы оцениваете эффективность. я бы измерил его с чистой скоростью и масштабируемостью. эти два не являются взаимоисключающими, однако, используя язык, который тесно связан с концепциями параллельных/распределенных вычислений и конструкциями языковых конструкций, в этом плане гораздо проще разрабатывать такие алгоритмы. конечно, вещь будет работать быстрее, если вы сделаете все рассылки самостоятельно, но эта ошибка подвержена и занимает много времени.

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

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

Если вы пишете программное обеспечение, единственное важное решение, которое нужно принять заранее, это дизайн... тогда вы его реализуете, и как только он будет реализован, вы сможете определить фактические узкие места, а затем оптимизировать там. как правило, более 90% вашего кода выполняется менее 10% времени... оптимизация важна только для оставшихся 10%... для их определения, вам нужно работать с программным обеспечением, которое вы можете профилировать... и вы нужно быстро, так что вы можете пересмотреть свой дизайн, прежде чем писать что-то супер оптимизированное, но дерьмо в противном случае...

Ответ 8

Это просто анекдотический вклад:

Я научил себя (некоторые) Clojure с помощью математических задач из Project Euler. Я решил некоторые проблемы, используя ленивые последовательности.

Я обнаружил, что при применении типов подсказок производительность для длинных вычислений (где запуск не имеет значения) обычно сопоставим с ручной настройкой Java.

Это почти можно ожидать: Clojure компилирует опережающий байт-код JVM и работает с той же оптимизирующей средой выполнения, что и Java-код.

Меня некоторое время поразило то, что некоторые вычисления занимали несколько ядер, хотя я не (сознательно) не запрограммировал ни на какие распараллеливания. Кажется, что среда выполнения может воспользоваться возможностью для распараллеливания генерации и потребления ленивых последовательностей.

Мой вывод: чистое функциональное программирование не должно быть медленнее, чем обязательное программирование, и иногда может обнаруживать неожиданные возможности оптимизации или распараллеливания.

Как человек, который ненавидит думать о concurrency, потому что это заставляет мой мозг болеть, я считаю "автоматическое распараллеливание" бесценной выгодой!

Ответ 9

Как правило, оптимизация компилятора отличает код, который:

  • Он фактически компилируется. Если в большинстве случаев нижняя часть стека вызовов находится внутри кода, который компилятор никогда не видит, он может оптимизировать все, что он хочет, но не будет иметь никакого эффекта.

  • Это потребляет значительную долю (например, 10% или более) времени выполнения, когда пользователь или другие пользователи действительно ждут программу. Если никто не ждет, если оптимизация никогда не будет замечена.

  • Здесь нет вызовов функций. Если скомпилированный код содержит вызовы функций, счетчик программ почти всегда находится в другом месте.

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

Ответ 10

В теории, поскольку компиляторы становятся более умными и умными, вы хотите программировать на языках высокого уровня, поскольку они не только позволят компилятору сделать больше оптимизации, но и для самой архитектуры CPU/памяти. Возможно, однажды у нас будут "развивающиеся процессоры", которые перестраиваются, чтобы наилучшим образом управлять определенным приложением. C предполагают, что компьютер имеет определенную архитектуру, называемую архитектурой фон Неймана, а функциональные/декларативные программы более независимы от архитектуры, поскольку вы заявляете, что нужно вычислять, а не как.