Haskell性能示例

我的代码中有这些基本类型:

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

Factored忽略FactFactored )。 我在不同层次上对代码进行基准测试,比较footbar的性能。 在基准, t = foo ,和bar只用于t通过newtype 。 因此,它们的运行时间应该基本相同,但标准报告foo需要9.2ns, t需要17.45ns的两倍,而bar需要高达268.1ns。

我已经尝试添加INLINABLE ,甚至是SPECIALIZE编译指示,但他们没有帮助。 我想相信GHC有一些神奇的语法/优化/等,可以一致地应用于解决这些类型的性能问题。 例如,我曾经看到过使用无点式编写代码会对性能产生巨大影响的情况。

完整的代码可以在这里找到。 我保证它不是吓人的。 这些模块是:

  • Foo:定义FoofooT
  • 酒吧:定义Barbar
  • FooBench:为foo定义一个基准
  • TBench:为t定义一个基准
  • BarBench:为bar定义基准
  • Main:运行三个基准
  • Factored:使用单例定义FactFactored
  • 大多数模块很小, 我在单独的文件中定义了三个基准,以便我可以检查它们的核心。 我生成了三个*Bench模块的核心,并将它们*Bench地对齐。 他们只有〜250行,第一〜200行是相同的,直到重命名。 问题是,我不知道最后50行左右应该怎么做。 FooBench vs TBench核心差异在这里, TBench vs BarBench的差异在这里,而FooBench vs BarBench的差异在这里。

    我只有几个问题:

  • 在高层次上,核心文件之间的本质区别是什么? 我正在寻找类似“在这里你可以看到GHC没有内联对x的调用。” 我应该寻找什么?

  • 可以做些什么来使三个基准都在9.2ns左右运行? GHC优化? INLINE / INLINABLE ? 我错过了SPECIALIZE pragmas? (你不能专门用于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相同。 (事实上​​, TBenchBarBench的核心是完全一样的,不包括类型签名)。

    我不完全确定为什么,但使用INLINE而不是INLINEABLE使tbarfoo执行相同。 我不是专家,但通常使用INLINE作为您确定要内联的小功能通常会更好。

    这就是说,我认为其中的一些问题是从基准测试的标准来制止ghc作弊。 例如,写bench "Bar" $ nf (GHC.Magic.inline bar) x你原来的代码有bar表演一样foo 。 我怀疑一个“真正的”计划不会那么微妙。

    链接地址: http://www.djcxy.com/p/33195.html

    上一篇: Haskell Performance by Example

    下一篇: collection pause time in a Haskell program