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

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

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

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

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

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

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

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

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

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

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

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


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

4b9b3361

Ответ 1

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

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

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

Ответ 2

Недавно опубликованная статья Haskell бьет C с использованием обобщенного потокового слияния от Geoff Mainland, Simon Peyton Jones, Simon Marlow, Roman Leshchinskiy (представлен в ICFP 2013) описывает такой пример. Аннотация (с интересной частью выделены жирным шрифтом):

Слияние потоков [6] - мощный метод автоматического преобразования высокоуровневые функции обработки последовательности в эффективные реализации. Он широко используется в библиотеках Haskell для манипулирования байтовыми массивами, текстом Unicode и нерасположенными векторами. Однако некоторые операции, такие как vector append, до сих пор не выполняются хорошо в рамках стандартного фьюжн-потока. Другие, как SIMD-вычисление с использованием инструкций SSE и AVX на современных чипах x86, похоже, не вписываются в рамки все.

В этой статье вводится обобщенная слияние потоков, которая решает эти проблемы. Ключевое понимание заключается в объединении нескольких потоковые представления, каждый из которых настроен для определенного класса потока потребитель. Мы также описываем представление потока, подходящее для эффективного вычисление с помощью инструкций SSE. Наши идеи реализованы в модифицированных версиях компилятора GHC и библиотеки vector. Тесты показывают, что высокоуровневый код Haskell, написанный с использованием наш компилятор и библиотеки могут создавать код, который быстрее, чем оба компилятор и вектор с вектором C.

Ответ 3

Это всего лишь примечание, а не ответ: gcc имеет атрибут pure, предполагающий, что он может учитывать чистоту; очевидные причины отмечаются в руководстве здесь.

Я бы подумал, что "статическое одиночное задание" накладывает форму чистоты - см. ссылки в http://lambda-the-ultimate.org/node/2860 или статью в википедии.

Ответ 4

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

Для изменений малого и среднего размера это может быть намного быстрее, чем строить с нуля.