Haskell使您可以派生类型类实例,例如:
{-# LANGUAGE DeriveFunctor #-}
data Foo a = MakeFoo a a deriving (Functor)
...但是,有时基准测试表明,如果手动实现typeclass实例并使用INLINE
注释type class方法,则性能会提高,如下所示:
data Foo a = MakeFoo a a
instance Functor Foo where
fmap f (MakeFoo x y) = MakeFoo (f x) (f y)
{-# INLINE fmap #-}
有没有办法做到两全其美?换句话说,是否有一种方法可以派生类型类实例并用INLINE
注释派生的类型类方法?
答案 0 :(得分:3)
尽管您无法像使用动态语言的类那样“重新打开” Haskell中的实例,但是仍有一些方法可以确保通过将某些标志传递给GHC来尽可能积极地内联函数。
-fspecialise-aggressively
消除了关于哪些功能可以特殊化的限制。任何重载的功能将是 专门使用此标志。这可能会产生很多 附加代码。
-fexpose-all-unfoldings
将包括接口文件中所有功能的(优化)展开,以便可以内联它们并 专门跨模块。结合使用这两个标志将具有几乎相同的效果 除了将以下事实标记为
INLINABLE
:INLINABLE
定义的展开未得到优化。
这些选项将允许GHC编译器内联fmap
。 -fexpose-all-unfoldings
选项尤其允许编译器出于内联目的将Data.Functor
的内部信息公开给程序的其余部分(这似乎提供了最大的性能优势)。这是我提出的一个快速而愚蠢的基准测试:
functor.hs
包含以下代码:
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE Strict #-}
data Foo a = MakeFoo a a deriving (Functor)
one_fmap foo = fmap (+1) foo
main = sequence (fmap (\n -> return $ one_fmap $ MakeFoo n n) [1..10000000])
编译时不带参数:
$ time ./functor
real 0m4.036s
user 0m3.550s
sys 0m0.485s
编译为-fexpose-all-unfoldings
:
$ time ./functor
real 0m3.662s
user 0m3.258s
sys 0m0.404s
这是此编译中的.prof
文件,以表明对fmap
的调用确实已内联:
Sun Oct 7 00:06 2018 Time and Allocation Profiling Report (Final)
functor +RTS -p -RTS
total time = 1.95 secs (1952 ticks @ 1000 us, 1 processor)
total alloc = 4,240,039,224 bytes (excludes profiling overheads)
COST CENTRE MODULE SRC %time %alloc
CAF Main <entire-module> 100.0 100.0
individual inherited
COST CENTRE MODULE SRC no. entries %time %alloc %time %alloc
MAIN MAIN <built-in> 44 0 0.0 0.0 100.0 100.0
CAF Main <entire-module> 87 0 100.0 100.0 100.0 100.0
CAF GHC.IO.Handle.FD <entire-module> 84 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding <entire-module> 77 0 0.0 0.0 0.0 0.0
CAF GHC.Conc.Signal <entire-module> 71 0 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding.Iconv <entire-module> 58 0 0.0 0.0 0.0 0.0
编译为-fspecialise-aggressively
:
$ time ./functor
real 0m3.761s
user 0m3.300s
sys 0m0.460s
同时带有两个标志:
$ time ./functor
real 0m3.665s
user 0m3.213s
sys 0m0.452s
这些小的基准测试绝对不能代表实际代码中的性能(或文件大小),但是它绝对表明您可以强制GHC编译器内联fmap
(并且它实际上可以具有对性能的影响不可忽略)。