Оптимизация кода F # или это действительно так медленно? - PullRequest
6 голосов
/ 08 февраля 2012

Я искал способ сделать правильное алгоритмическое кодирование с использованием .NET со всеми преимуществами современных языков (например, мне нравится строгая проверка типов, перегрузка операторов, лямбда-выражения, универсальные алгоритмы).Обычно я пишу свои алгоритмы (в основном, обработку изображений) на C ++.Поскольку язык F # кажется довольно интересным, я немного поиграл, но, похоже, он очень медленный.В качестве одного из самых простых тестов я только что провел некоторые манипуляции с массивами -> увеличение яркости изображения:

let r1 = rgbPixels |> Array.map (fun x -> x + byte(10) )

Кажется, что это по крайней мере в 8 раз медленнее, чем в сравнении с реализацией C ++ - еще хуже для большегосложные алгоритмы, например, 2D свертка.Есть ли какой-нибудь более быстрый способ или я пропускаю какие-то конкретные настройки компилятора (да, сборка релиза с оптимизацией на ...)?Я готов заплатить за хорошую и высокую абстракцию, но такие накладные расходы не очень хороши (мне нужно было бы распараллелить на 8 ядрах, чтобы компенсировать это :)) - по крайней мере, это разрушает мотивацию учиться дальше ... Мой другой вариантбыло бы оставить мои более тяжелые алгоритмы в C ++ и взаимодействовать с управляемым C ++, но это нехорошо, так как поддержка управляемой оболочки была бы довольно обременительной.

Ответы [ 2 ]

6 голосов
/ 08 февраля 2012

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

РЕДАКТИРОВАТЬ: я понятия не имею, почему, но простые тесты следующего кода дают худшие результаты для Array.map. Обязательно профилируйте любой алгоритм, который вы пробуете при выполнении такого рода оптимизаций. Однако я получил очень похожие результаты между for и map.

Array.map создает новый массив для результата операции, вместо этого вы хотите Array.iteri.

rgbPixels |> Array.iteri (fun i x -> rgbPixels.[i] <- x + 10uy)

Обратите внимание, что это может быть заключено в вашем собственном модуле, как показано ниже

module ArrayM =
    let map f a = a |> Array.iteri (fun i x -> a.[i] <- f x)

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

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

5 голосов
/ 08 февраля 2012

Я думаю, что можно с уверенностью сказать, что идиоматический F # часто не соответствует производительности оптимизированного C ++ для манипулирования массивами по нескольким причинам:

  1. Доступ к массиву проверяется по границам массива в .NET для обеспечения безопасности памяти. JIT-компилятор CLR способен исключить проверки границ для некоторого стереотипного кода, но для этого обычно требуется использовать цикл for с явными границами, а не более идиоматические конструкции F #.
  2. Обычно при использовании абстракций, таких как лямбда-выражения, накладываются незначительные накладные расходы (например, fun i -> ...). В тесных циклах эти небольшие накладные расходы могут оказаться значительными по сравнению с работой, выполняемой в теле цикла.
  3. Насколько мне известно, JIT-компилятор CLR не использует преимущества команд SSE в той же степени, в которой способны компиляторы C ++.

На другой стороне бухгалтерской книги,

  1. У вас никогда не будет переполнения буфера в коде F #.
  2. Ваш код будет легче рассуждать. Как следствие, для данного уровня общей сложности кода вы часто можете реализовать более сложный алгоритм в F #, чем в C ++.
  3. При необходимости вы можете написать однотипный код, который будет работать со скоростью, близкой к скорости C ++, а также есть средства для взаимодействия с небезопасным кодом C ++, если вы обнаружите, что вам нужно написать C ++ для удовлетворения ваших требований к производительности.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...