F # кажется медленнее, чем другие языки... что я могу сделать, чтобы ускорить его?
Мне нравится F #; Я действительно, правда. Будучи укушенным "функциональным программированием", я заставляю себя использовать его, когда у меня есть возможность. Фактически, я недавно использовал его (в течение одной недели) для код хорошего алгоритма AI.
Однако мои попытки до сих пор (см. вопрос SO, связанный с моей первой попыткой здесь), как представляется, указывают на то, что, хотя, несомненно, красиво... F # имеет самый медленный скорость выполнения всех языков, которые я использовал.
Я делаю что-то не так в своем коде?
Я подробно объясню, что я сделал в своем сообщении в блоге, и в моих экспериментах я вижу OCaml и остальную группу, работающую где угодно от 5x до 35x быстрее, чем F #.
Я единственный, у кого такие переживания? Я нахожу это обескураживающим, что язык, который мне больше всего нравится, также самый медленный - иногда далеко...
EDIT: прямая ссылка GitHub, где код живет в разных языковых формах...
EDIT2: благодаря Томасу и Даниэлю скорость значительно улучшилась:
- Наибольшее ускорение скорости: переход от "ref" к "mutable" дал колоссальные 30%.
- Удаление исключений и использование while/flagChecks дали еще 16%.
- Переключение с дискриминированных объединений на перечисления дало еще 5%.
- "inline" дал 0.5-1%
EDIT3: Д-р Джон Харроп присоединился к бою: 60% ускорение, заставив ScoreBoard работать непосредственно с "перечислимой" версией данных. Настоящая версия F # теперь работает в 3-4 раза медленнее, чем С++, что является хорошим результатом для среды выполнения на базе VM. Я считаю, что проблема решена - спасибо, ребята!
EDIT4: после слияния всех оптимизаций это результаты (F # достиг С# в императивном стиле - теперь, если бы я мог что-то делать с функциональным стилем!)
- real 0m0.221s: Это был С++
- real 0m0.676s: Это был С# (императив, С++-зеркало)
- real 0m0.704s: Это F # (императив, зеркало С++)
- real 0m0.753s: Это был OCaml (императив, С++-зеркало)
- real 0m0.989s: это OCaml (функциональный)
- real 0m1.064s: Это была Java (императив)
- real 0m1.955s: Это был F # (функциональный)
Ответы
Ответ 1
Если вы не можете дать образец кода разумного размера, это трудно сказать. В любом случае, императивная версия F # должна быть такой же эффективной, как и императивная версия С#. Я думаю, что один из подходов состоит в том, чтобы сравнить эти два, чтобы увидеть, что вызывает разницу (тогда кто-то может помочь в создании этого бита быстрее).
Я кратко рассмотрел ваш код и вот несколько различных (непроверенных) предложений.
-
Вы можете заменить дискриминированный union Cell
на enum (это означает, что вы будете использовать типы значений и целочисленное сравнение вместо ссылочных типов и тестов типа времени выполнения):
type Cell =
| Orange = 1
| Yellow = 2
| Barren = 3
-
Вы можете отметить некоторые тривиальные функции как inline
. Например:
let inline myincr (arr:int array) idx =
arr.[idx] <- arr.[idx] + 1
-
Не используйте исключения для потока управления. Это часто делается в OCaml, но исключения .NET медленны и должны использоваться только для исключений. Вы можете заменить цикл for
в своем примере контуром while
и изменчивым флагом или с помощью функции хвостового рекурсивного (хвостовая рекурсивная функция скомпилирована в цикл, поэтому она будет эффективной даже в императивном решении).
Ответ 2
Это не ответ, сам по себе, но вы пробовали написать тот же самый код в F # и С#, т.е. императивный код F #? Скорость должна быть одинаковой. Если вы сравниваете краткий функциональный код с интенсивным использованием функций более высокого порядка, выражениями последовательности, ленивыми значениями, сложным сопоставлением шаблонов и т.д. - все, что позволяет более короткий, понятный (читаемый, более обслуживаемый) код - часто бывает компромисс. Как правило, время разработки/обслуживания намного больше времени выполнения, поэтому обычно считается желательным компромиссом.
Некоторые ссылки:
F # и С# CLR то же самое, почему F # быстрее, чем С#
С#/F # Сравнение производительности
https://stackoverflow.com/info/142985/is-a-program-f-any-more-efficient-execution-wise-than-c
Еще один момент для рассмотрения: на функциональном языке вы работаете на более высоком уровне, и становится очень легко упускать из вида затраты на операции. Например, Seq.sort
кажется достаточно невинным, но наивное его использование может обречь производительность. Я бы порекомендовал ваш код, задавая себе вопрос, если вы понимаете стоимость каждой операции. Если вы не чувствуете отражения, более быстрый способ сделать это, конечно, с помощью профилировщика.