我一直在通过实现一个特征选择算法来学习一些Haskell。

我从20秒开始将基准数据集的性能降低到5s,其中C程序在0.5s内处理相同的数据集。 数据集可以在这里找到。 要运行,请调用已编译的二进制文件: ./Mrmr 10 test_nci9_s3.csv

代码在这里 ,我有兴趣优化mutualInfoInnerLoop:

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]

将大多数参数显示为Lazily评估和装箱(与严格和未装箱相反)。

我尝试过BangPatterns,我尝试过MagicHash,但我似乎无法让它变得更快。

有人有什么建议吗?

===============>>#1 票数:2

到目前为止,我还没有专家,但我确实看到了一个小改进。 在你的来源中我看到了这个:

mutualInfo n ... = foldl' (mutualInfoInnerLoop n $ U.zip xs ys) ...

每次调用函数时都不需要检查n == 0 ,因为在调用函数时从不更改n参数。 xys说法并没有改变,或者说,这意味着pxy不会在调用改变,因为它完全取决于xysn 让我们利用这些东西来确保创建一个只关闭这些东西的闭包。

mutualInfoInnerLoop n xys
  | n == 0 || pxy == 0 = const
  | otherwise          = go
  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
        go !acc (!i, !j, !px_py)
          | px_py == 0 = acc
          | otherwise  = pxy * logBase 2 ( pxy / px_py ) + acc

我不确定GHC是否足够智能自己进行优化,我也不确定这会节省多少时间/空间,但这是我所拥有的最好的。 随着那些爆炸模式遍布各地,我想知道这是否是一个过于严格的情况。

  ask by LanceH translate from so

未解决问题?本站智能推荐: