Изменение строгого поля записи приводит к ухудшению производительности
У меня есть программа, которая использует 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
s.
С помощью этого кода моя программа (извините, я не могу ее разделить) работает в 163s.
Теперь измените одну строку, например
, srcSpanStartLine :: !Int
I.e., поле srcSpanStartLine
теперь отмечено как строгое. Моя программа теперь занимает 198 секунд. Поэтому, чтобы одно поле строго меняло время работы примерно на 20%.
Как это возможно? Код для функции SrcSpan
должен быть таким же, несмотря на то, что он уже строг. Код для селектора srcSpanStartLine
должен быть немного проще, поскольку его больше не нужно оценивать.
Я экспериментировал с -funbox-strict-fields
и -funbox-small-strict-field
вкл. и выкл. Это не делает заметной разницы. Я использую ghc 7.8.3.
Кто-нибудь видел что-то подобное? Любые яркие идеи, что может вызвать это?
Ответы
Ответ 1
С еще одним исследованием я могу ответить на свой вопрос. Короткий ответ uniplate.
Слегка длинный ответ. В одном месте я использовал uniplate для получения дочерних элементов типа Pat
(haskell-src-exts для шаблонов). Вызов выглядел как children p
, а тип этого экземпляра children
был Pat SrcSpanInfo -> [Pat SrcSpanInfo]
. Таким образом, он не выполняет рекурсию, просто возвращая непосредственные дочерние элементы node.
Uniplate использует два очень разных метода в зависимости от того, существуют ли строгие поля в типе, в котором вы работаете. Без строгих полей это разумно быстро, со строгими полями он переключается на использование gfoldl
и невероятно медленно. И хотя мое использование uniplate напрямую не связано с строгим полем, оно замедлилось.
Заключение: Остерегайтесь uniplate, если у вас есть строгое поле в любом месте!