Показатели производительности стиля Point-Free
Im, берущий мои первые детские шаги в обучении функциональной программированию с использованием F # и Ive, просто встречается с операторами Forward Pipe (| > ) и Forward Composition ( → ). Сначала я думал, что они были просто сахаром, а не влияют на конечный код (хотя я знаю, что трубопровод помогает с типом вывода).
Однако я столкнулся с этой статьей SO:
Каковы преимущества и недостатки стиля "point free" в функциональном программировании?
Что имеет два интересных и информативных ответа (что вместо упрощения вещей для меня открыло целую банку червей, окружающих "точечный" или "бессмысленный" стиль). Мой прием от этих (и других чтений) - это то, что без точек является обсуждаемой областью. Как и lambas, стиль без точек может сделать код более понятным или намного сложнее, в зависимости от использования. Это может помочь в значительном именовании вещей.
Но мой вопрос касается комментария к первому ответу:
AshleyF размышляет в ответе:
"Мне кажется, что композиция может уменьшить давление в ГК, сделав более очевидным для компилятора то, что нет необходимости производить промежуточные значения, как при конвейерной обработке, что помогает сделать так называемую проблему" обезлесения "более приемлемой".
Gasche отвечает:
"Часть улучшенной компиляции вообще не верна. В большинстве языков стиль без надписи фактически снижает производительность. Haskell в значительной степени полагается на оптимизацию, потому что это единственный способ снизить стоимость этих вещей. В лучшем случае, эти комбинаторы отклонены и вы получите эквивалентную точную версию"
Может ли кто-нибудь расширить влияние на производительность? (В общем и конкретно для F #) я только что предположил, что это была вещь в стиле письма, и компилятор будет переводить обе идиомы в эквивалентный код.
Ответы
Ответ 1
Этот ответ будет F # -специфичным. Я не знаю, как работают внутренние функции других функциональных языков, и тот факт, что они не компилируются в CIL, может иметь большое значение.
Здесь я вижу три вопроса:
- Каковы последствия использования
|>
?
- Каковы последствия использования
>>
?
- Какова разница в производительности между объявлением функции с ее аргументами и без них?
Ответы (используя примеры из вопроса, который вы связали с):
-
Есть ли разница между x |> sqr |> sum
и sum (sqr x)
?
Нет, нет. Скомпилированный CIL точно такой же (здесь представлен в С#):
sum.Invoke(sqr.Invoke(x))
(Invoke()
используется, потому что sqr
и sum
не являются методами CIL, они FSharpFunc
, но это не имеет значения здесь.)
-
Есть ли разница между (sqr >> sum) x
и sum (sqr x)
?
Нет, оба образца скомпилируются с тем же CIL, что и выше.
-
Есть ли разница между let sumsqr = sqr >> sum
и let sumsqr x = (sqr >> sum) x
?
Да, скомпилированный код отличается. Если вы укажете аргумент, sumsqr
будет скомпилирован в обычный метод CLI. Но если вы не укажете его, он скомпилируется как свойство типа FSharpFunc
с полем поддержки, метод Invoke()
содержит код.
Но эффект всего заключается в том, что вызов бесплатной версии означает загрузку одного поля (FSharpFunc
), которое не выполняется, если вы укажете аргумент. Но я думаю, что это не должно заметно влиять на производительность, за исключением самых экстремальных обстоятельств.