Я изучил некоторые Haskell, выполнив алгоритм выбора функций.
Я получил производительность с 20-х годов на базовом наборе данных до 5 с, где программа C обрабатывает один и тот же набор данных в 0,5 с. Набор данных можно найти здесь. Для запуска вызовите скомпилированный двоичный файл следующим образом: ./Mrmr 10 test_nci9_s3.csv
.
Код здесь, и меня интересует оптимизация взаимного интерфейса:
mutualInfoInnerLoop :: Double -> Data.Vector.Unboxed.Vector (Int, Int) -> Double -> (Int, Int, Double) -> Double
mutualInfoInnerLoop n xys !acc (!i, !j, !px_py)
| n == 0 || px_py == 0 || pxy == 0 = acc
| otherwise = pxy * logBase 2 ( pxy / px_py ) + acc
where
pxy = ( fromIntegral . U.foldl' accumEq2 0 $ xys ) / n
accumEq2 :: Int -> (Int, Int) -> Int
accumEq2 !acc (!i', !j')
| i' == i && j' == j = acc + 1
| otherwise = acc
Профилер говорит:
COST CENTRE MODULE %time %alloc
mutualInfoInnerLoop Main 75.0 47.9
mutualInfo Main 14.7 32.1
parseCsv Main 5.9 13.1
CAF GHC.Float 1.5 0.0
readInt Main 1.5 1.2
doMrmr Main 1.5 4.0
Что показывает, что mutualInfoInnerLoop составляет 50% от распределений, причем 75% времени выполнения программы. Распределения сбивают с толку.
Кроме того, ядро для этой функции имеет подпись:
mutualInfoInnerLoop_rXG
:: GHC.Types.Double
-> Data.Vector.Unboxed.Base.Vector (GHC.Types.Int, GHC.Types.Int)
-> GHC.Types.Double
-> (GHC.Types.Int, GHC.Types.Int, GHC.Types.Double)
-> GHC.Types.Double
[GblId,
Arity=4,
Caf=NoCafRefs,
Str=DmdType U(L)LU(L)U(U(L)U(L)U(L))m]
Показание большинства параметров как лениво оценено и в коробке (в отличие от строгих и unboxed).
Я пробовал BangPatterns, я пробовал MagicHash, и я не могу заставить его двигаться быстрее.
У кого-нибудь есть предложения?