用GHC保证专业化
我正在努力确保GHC专门设计一个递归函数,以便所有东西都可以被拆箱。 完整的示例代码(以及GHC内核的转储)在本要点中可用。 有问题的功能如下所示:
import Data.Bits
import qualified Data.Vector.Unboxed as UV
lookupSorted :: Ord a => (Int -> a) -> Int -> a -> Maybe Int
lookupSorted lookupIx len needle =
let !r = go 0 (len - 1)
in if r < 0 then Nothing else Just r
where
go :: Int -> Int -> Int
go !lo !hi = if lo <= hi
then
let !mid = lo + (unsafeShiftR (hi - lo) 1)
!val = lookupIx mid
in case compare val needle of
EQ -> mid
LT -> go (mid + 1) hi
GT -> go lo (mid - 1)
else (-1)
这是一种算法,它查找来自任何已排序容器的值,而不是可以索引到的值。 我想确保的两个功能是这样的专用版本:
{-# NOINLINE lookupUnboxedWord #-}
lookupUnboxedWord :: UV.Vector Word -> Word -> Maybe Int
lookupUnboxedWord v w = lookupSorted (UV.unsafeIndex v) (UV.length v) w
{-# NOINLINE lookupUnboxedDouble #-}
lookupUnboxedDouble :: UV.Vector Double -> Double -> Maybe Int
lookupUnboxedDouble v w = lookupSorted (UV.unsafeIndex v) (UV.length v) w
好消息是,从看到被倾销的核心,我可以看到GHC已经在执行我感兴趣的专业化,这非常令人印象深刻。 不过,我希望能够指望它发生。 我担心如果我为该文件添加足够多的专用变体,或者如果我从另一个模块调用lookupSorted
,GHC最终可能倾向于生成一个小的可执行文件,而不是一个快速的可执行文件。
我的理解是SPECIALIZE
pragma在这种情况下无助。 GHC目前不允许基于价值观点的专业化。 我很确定,如果我愿意为索引操作编写类型类型,那么我可以使SPECIALIZE
工作。 我试图避免这种方法,因为除非没有其他解决方案,否则我不想引入类型类。
有没有办法强制GHC创建我的函数的这些专用变体? 此外,如果任何人对转储的核心文件有任何评论(如果有什么不是最佳的),我将不胜感激任何反馈。 谢谢。
- - 编辑 - -
更多地思考这个问题,似乎只需在lookupSorted
上放置一个INLINE
lookupSorted
注lookupSorted
。 GHC文档并不清楚INLINE
和递归绑定内部let
或where
子句之间的相互作用。 任何关于此的澄清,希望有一个支持来源,可能会有所帮助。
你最后的观察是正确的:如果你在一个函数上放置了一个INLINE
注解,当它有足够的参数进行调用时,它将被内联。
足够的参数意味着你的函数的参数数量在=
的左边(而不是右边的lambdas)。 这可以让你做类似的事情
foo op n = y -> go n y
where go acc i = … op …
fooSpec1 = foo (+) 0
fooSpec2 = foo (*) 1
并获得foo
两个专业化,然后您可以多次调用,而无需进一步重复代码。
对于所有这些,在where
发生什么并不重要,递归函数只会与foo
一起内联。
(对不起,没有消息来支持。)
链接地址: http://www.djcxy.com/p/33199.html上一篇: Guarantee of Specialization with GHC
下一篇: Specializing related polymorphic functions without inlining