将单个记录字段更改为严格会导致性能下降

时间:2014-08-01 22:35:17

标签: haskell

我有一个使用haskell-src-exts的程序,为了提高性能,我决定严格控制一些记录字段。这导致了更糟糕的表现。

这是我正在改变的完整模块:

{-# LANGUAGE DeriveDataTypeable, BangPatterns #-}
module Cortex.Hackage.HaskellSrcExts.Language.Haskell.Exts.SrcSpan(
    SrcSpan, srcSpan, srcSpanFilename, srcSpanStartLine,
    srcSpanStartColumn, srcSpanEndLine, srcSpanEndColumn,
    ) where

import Control.DeepSeq
import Data.Data

data SrcSpan = SrcSpanX
    { srcSpanFilename    :: String
    , srcSpanStartLine   :: Int
    , srcSpanStartColumn :: Int
    , srcSpanEndLine     :: Int
    , srcSpanEndColumn   :: Int
    }
  deriving (Eq,Ord,Show,Typeable,Data)

srcSpan :: String -> Int -> Int -> Int -> Int -> SrcSpan
srcSpan fn !sl !sc !el !ec = SrcSpanX fn sl sc el ec

instance NFData SrcSpan where
    rnf (SrcSpanX x1 x2 x3 x4 x5) = rnf x1

请注意,构建SrcSpan的唯一方法是使用srcSpan函数,该函数在所有Int中都是严格的。

使用此代码我的程序(抱歉,我无法分享)在163s运行。

现在更改一行,例如

    , srcSpanStartLine   :: !Int

即,srcSpanStartLine字段现在标记为严格。我的程序现在运行198秒。因此,严格控制一个字段会使运行时间增加约20%。

这怎么可能? srcSpan函数的代码应该是相同的,因为它已经是严格的。 srcSpanStartLine选择器的代码应该更简单一些,因为它不再需要进行评估。

我开始尝试-funbox-strict-fields-funbox-small-strict-field。它没有任何明显的区别。我正在使用ghc 7.8.3。

有没有人见过类似的东西?什么可能导致它的好主意?

1 个答案:

答案 0 :(得分:15)

通过更多调查,我可以回答我自己的问题。简短的回答是uniplate

稍微长一点的答案。在一个地方,我使用uniplate来获取Pat的孩子(haskell-src-exts类型的模式)。该调用看起来像children p,此children实例的类型为Pat SrcSpanInfo -> [Pat SrcSpanInfo]。所以它不进行递归,只返回节点的直接子节点。

Uniplate使用两种截然不同的方法,具体取决于您的操作类型中是否存在严格字段。如果没有严格的字段,它会很快合理,严格的字段会切换到使用gfoldl并且非常慢。即使我使用uniplate没有直接涉及严格的字段,它也放慢了速度。

结论:如果你在任何地方都有一个严格的字段,请注意单独显示!