Изменение строгого поля записи приводит к ухудшению производительности

У меня есть программа, которая использует 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, если у вас есть строгое поле в любом месте!