Как стать автором
Обновить

Комментарии 21

1. Уже 7.6.2
2. Вот это меня в Haskell и удручает, что для должной производительности приходится испещрять код всякими {-# INLINE f #-}, {-# UNPACK #-} и явными определениями переменных, которые нужно посчитать только один раз. Что средства, с которых начинается обучение и которые более всего свойственны языку (списки, Maybe, Either) медленны.
Да, отсутствие флага вроде -finline-functions-by-default — серьезное упущение. {-# NOINLINE #-} же есть.
П.2 явное преувеличение, dataflow-Фреймворки пишут не так и часто. Ну и Haskell не за производительность кода выбирают. Не встречался с обычными задачами, где тормоза были бы именно в Maybe/Either, а представить себе алгоритм, который упирался бы в обработку Maybe/Either сходу не могу.
Проблема в том, что клиентский код на этом фреймворке тоже приходится писать крайне внимательно, с прагмами и явными сигнатурами функций.
Это всё же достаточно специфичная задача, в таких задачах и на Си++ наивно не попишешь. То, что для такого языка можно получить быстрый код хотя бы так уже хорошо. Можно не бояться, что придётся переписывать всё из-за тормозов.
Нередко, кстати, встречается другое заблуждение, что на C++ можно получить быстрый код нахаляву, а меж тем наивный код на C++ и Haskell работают сравнимо, а именно такого кода больше всего.
Как раз скорость наивного кода более всего и интересна, т.е. насколько будет тормозить обычный код, и не придётся ли из-за этого всё переписывать.
Это надо очень сильно постараться, чтобы написать на C++ так же медленно, как на «дефолтном» Хаскеле.

И на мой взгляд вопрос в Хаскеле не сколько в наивности, столько в бойлерплейте.
> Это надо очень сильно постараться, чтобы написать на C++ так же медленно, как на «дефолтном» Хаскеле.
Почему вы так считаете?

Я не делал специального сравнения, но не раз натыкался на различные типичные задачи, где наивные решения на C++ и Haskell были сравнимы с переменным лидерством.
Сомневаюсь:
1) Откажитесь почти от всего STL. std::string, std::vector и std::map — это слишком быстро для Хаскеля. Только std::forward_list, только хардкор.
2) Где в C++ обычное тело цикла, в Хаскеле вызов функции. Причем вызов в Хаскеле — это не просто аргументы на стек и call, там же еще рантайм таскает по стеку туда-сюда кучу внутренних переменных.

И так далее…
Я склонен не доверять предсказаниям, потому что тормозит, как известно, не там, где думает программист.
Так что это будет верно только если считать, что в Haskell тормозит всё и везде.

Про std::forward_list не понял, если честно. Или ByteString и Text это сродни forward_list, по-вашему?
В «дефолтном» Хаскеле для строк применяется type String = [Char].
А причём тут дефолтный Хаскель? Речь о «наивном» реально используемом коде, а там уже давным давно стандарт ByteString/Text.
Ах, если бы. Даже на hackage меньше половины пакетов зависят от bytestring либо text. (Всего пакетов, по данным Hayoo, 4750.)
Как будто оставшаяся половина активно работает со строками и String будет там тормозить.
Ок, тут вы правы, ByteString народ похоже использовать приучился :)
На самом деле, внятных хороших исследований этого вопроса я не видел, меряют на каких-то синтетических тестах да исходят из посылов, что на C++ всё быстрее по умолчанию, поэтому всё довольно туманно.
> Это надо очень сильно постараться, чтобы написать на C++ так же медленно, как на «дефолтном» Хаскеле.

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

Вместо INLINE зачастую лучше INLINABLE, чтобы GHC сам решал инлайнить, или нет.

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

Насчет INLINABLE не согласен, GHC очень печется о лишней паре килобайт бинарника и сам почти ничего не инлайнит.
Тут все зависит от типа разрабатываемой программы. Для числодробильни важно инлайнить, разворачивать циклы, не делать лишних ветвлений и вообще ничего лишнего не делать. А для программ с более-менее развесистой логикой ленивость может запросто выиграть, т.к. не будет вычислять то, что не нужно.

GHC, возможно, не зря печется о лишних килобайтах, т.к. не всегда инлайн дает ускорение. Посмотрите на исходники того же Data.Map. Там именно INLINABLE и не везде.
Вообще у INLINABLE кажется немного другая семантика, с кросс-модульной компиляцией связанная. Так-то GHC и функции совсем без прагмы решает, встраивать или нет, теоретически.
Для строк есть Text, байтстринги (что и следует из названия) предназначены для хранения последовательностей байт, или вы за пределы ASCII не вылезаете и
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации