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 кажется достаточно невинным, но наивное его использование может обречь производительность. Я бы порекомендовал ваш код, задавая себе вопрос, если вы понимаете стоимость каждой операции. Если вы не чувствуете отражения, более быстрый способ сделать это, конечно, с помощью профилировщика.