我有一个使用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。
有没有人见过类似的东西?什么可能导致它的好主意?
答案 0 :(得分:15)
通过更多调查,我可以回答我自己的问题。简短的回答是uniplate。
稍微长一点的答案。在一个地方,我使用uniplate来获取Pat
的孩子(haskell-src-exts类型的模式)。该调用看起来像children p
,此children
实例的类型为Pat SrcSpanInfo -> [Pat SrcSpanInfo]
。所以它不进行递归,只返回节点的直接子节点。
Uniplate使用两种截然不同的方法,具体取决于您的操作类型中是否存在严格字段。如果没有严格的字段,它会很快合理,严格的字段会切换到使用gfoldl
并且非常慢。即使我使用uniplate没有直接涉及严格的字段,它也放慢了速度。
结论:如果你在任何地方都有一个严格的字段,请注意单独显示!