cro*_*eea 12 performance haskell ghc
我的代码中有这些基本类型:
newtype Foo (m :: Factored) = Foo Int64 deriving (NFData)
foo :: forall m . (Fact m) => Foo m -> Foo m
class T t where t :: (Fact m ) => t m -> t m
instance T Foo where t = foo
newtype Bar t (m :: Factored) = Bar (t m) deriving (NFData)
bar :: (Fact m, T t) => Bar t m -> Bar t m
bar (Bar v) = Bar $ t v
(忽略Fact并Factored暂时).我标杆不同层次的代码,比较的性能foo,t和bar.在基准,t = foo和bar只用于t通过newtype.因此,它们的运行时应基本相同,但标准报告foo需要9.2ns,t大约是17.45ns的两倍,并且bar需要高达268.1ns.
我已尝试添加INLINABLE甚至是一个SPECIALIZEpragma,但他们没有帮助.我想相信GHC有一些神奇的语法/优化/等可以一致地应用于解决这些类型的性能问题.例如,我已经看到过以无点样式编写代码具有显着性能影响的情况.
完整的代码可以在这里找到.我保证这不会令人生畏.这些模块是:
Foo,foo和TBar和barfootbarFact和Factored使用单身人士大多数模块很小; 我在单独的文件中定义了三个基准,以便我可以检查它们的核心.我为这三个*Bench模块生成了核心,并尽可能地将它们对齐.它们每个只有~250行,前两行是相同的,直到重命名.问题是我不知道该怎么做最后50行.在(核)扩散的FooBenchVS TBench是在这里,参与比较TBenchVS BarBench是在这里,并为DIFF FooBenchVS BarBench是在这里.
我只有几个问题:
从较高的层面来看,核心文件之间的本质区别是什么?我正在寻找类似" 在这里你可以看到GHC没有内联电话x." 我应该寻找什么?
如何才能使这三个基准测试在约9.2ns内完成?GHC优化?INLINE/ INLINABLEpragmas?SPECIALIZE我错过了pragma?(您不能专门用于F128::Factored;在我的真实库中,此值可能会在运行时生效.)限制/延迟内联到特定阶段?
虽然我正在寻找一个实际的解决方案来快速完成基准测试,但是这个例子的技巧可能无法扩展到我的真实库.因此,我也在寻找特定技术应该起作用的"高级"解释.
首先,看看bar:
bar :: (Fact m, T t) => Bar t m -> Bar t m
bar (Bar v) = Bar $ t v
我们可以在不需要参数的情况下编写这个coerce:
bar :: (Fact m, T t) => Bar t m -> Bar t m
bar = (coerce :: (t m -> t m) -> Bar t m -> Bar t m) t
这(我们希望)得到的bar表现与之相同t.(实际上核心TBench和BarBench完全相同,不包括类型签名).
我不完全确定为什么,但使用INLINE而不是INLINEABLE制作t和bar执行相同foo.我不是专家,但通常最好使用INLINE您确定要内联的小功能.
也就是说,我认为其中一些问题来自于基准如何停止ghc作弊.例如,bench "Bar" $ nf (GHC.Magic.inline bar) x在原始代码上bar执行的操作与执行相同foo.我怀疑一个"真正的"程序不会那么微妙.