来自GHC 7.6 的文档:
[Y]你通常甚至不需要SPECIALIZE pragma.在编译模块M时,GHC的优化器(带-O)自动考虑在M中声明的每个顶级重载函数,并将其专门用于在M中调用它的不同类型.优化器还考虑每个导入的INLINABLE重载函数,并将其专门用于M中调用的不同类型.
和
此外,给定函数f的SPECIALIZE编译指示,GHC将自动为f调用的任何类型类重载函数创建特殊化,如果它们与SPECIALIZE编译指示位于同一模块中,或者它们是INLINABLE; 等等,过渡性的.
因此GHC应该自动专门化一些INLINABLE
没有编译指示标记的/大多数/所有(?)函数,如果我使用显式编译指示,则特化是可传递的.我的问题是:auto -specialization 是否具有传递性?
具体来说,这是一个小例子:
Main.hs:
import Data.Vector.Unboxed as U
import Foo
main =
let y = Bar $ Qux $ U.replicate 11221184 0 :: Foo (Qux Int)
(Bar (Qux ans)) = iterate (plus y) y !! 100
in putStr $ show $ foldl1' (*) ans
Run Code Online (Sandbox Code Playgroud)
Foo.hs:
module Foo (Qux(..), Foo(..), plus) where
import Data.Vector.Unboxed as U
newtype Qux r = Qux (Vector r)
-- …
Run Code Online (Sandbox Code Playgroud) 这是场景:我已经编写了一些带有类型签名的代码,并且GHC抱怨无法推断x~y某些x
和y
.你通常可以将GHC作为一个骨骼并简单地将同构函数添加到函数约束中,但这有几个原因:
我只花了几个小时与案例3作斗争.我正在玩syntactic-2.0
,我试图定义一个与域无关的版本share
,类似于在中定义的版本NanoFeldspar.hs
.
我有这个:
{-# LANGUAGE GADTs, FlexibleContexts, TypeOperators #-}
import Data.Syntactic
-- Based on NanoFeldspar.hs
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
Domain a ~ sup,
Domain b ~ sup,
SyntacticN (a -> (a -> b) -> b) fi)
=> a -> (a -> b) -> a
share = sugarSym Let
Run Code Online (Sandbox Code Playgroud)
和GHC could not …
我开始理解forall
关键字如何在所谓的"存在类型"中使用,如下所示:
data ShowBox = forall s. Show s => SB s
Run Code Online (Sandbox Code Playgroud)
然而,这仅仅是如何forall
使用的一个子集,我根本无法将其用于这样的事情:
runST :: forall a. (forall s. ST s a) -> a
Run Code Online (Sandbox Code Playgroud)
或解释为什么这些是不同的:
foo :: (forall a. a -> a) -> (Char, Bool)
bar :: forall a. ((a -> a) -> (Char, Bool))
Run Code Online (Sandbox Code Playgroud)
或整个RankNTypes
东西......
我倾向于选择清晰,无术语的英语而不是学术环境中常见的语言.我尝试阅读的大部分解释(我可以通过搜索引擎找到的解释)存在以下问题:
runST
,foo
和bar
以上).所以...
关于实际问题.任何人都可以完全解释的forall
清晰,简单的英语关键字(或者,如果它存在于某个地方,点到我已经错过了这样一个明确的解释),不承担我在行话悠久的数学家?
编辑添加:
下面有高质量的答案有两个突出的答案,但不幸的是我只能选择一个最好的答案. 诺曼的回答很详细的和有用的,解释的方式,表现出一定的理论基础的东西forall
,并在同一时间向我展示了一些它的实际影响. yairchu的回答覆盖了其他人没有提到的范围(范围类型变量),并用代码和GHCi会话说明了所有概念.我愿意选择两者是最好的.不幸的是,我不能和上,俯瞰着两个答案后密切,我已经决定,yairchu的稍微挤掉了诺曼,因为说明性代码和附加的说明.这是一个有点不公平,但是,因为我真的需要这两个答案,了解这一点是一点forall
没给我留下了恐惧的隐隐感觉,当我看到它在类型签名.
Haskell(使用GHC
编译器)比你期望的要快很多.如果使用得当,它可以与低级语言保持密切联系.(Haskellers最喜欢的事情是尝试在C的5%范围内(或者甚至击败它,但这意味着你使用的是低效的C程序,因为GHC将Haskell编译为C).)我的问题是,为什么?
Haskell是声明性的,基于lambda演算.机器架构显然势在必行,粗略地基于图灵机.实际上,Haskell甚至没有具体的评估顺序.此外,您不必处理机器数据类型,而是始终生成代数数据类型.
最奇怪的是更高阶函数.你会认为动态创建函数并抛出它们会使程序变慢.但是使用更高阶函数实际上使Haskell更快.实际上,似乎要优化Haskell代码,你需要使它更优雅和抽象,而不是更像机器.如果Haskell的更高级功能没有改进,它们似乎甚至都不会影响它的性能.
很抱歉,如果这听起来不错,但这是我的问题:为什么Haskell(使用GHC编译)如此之快,考虑到它的抽象性质和与物理机器的差异?
注意:我说C和其他命令式语言有点类似于图灵机的原因(但不是Haskell类似于Lambda微积分)是因为在命令式语言中,你有一个有限数量的状态(也就是行号) ,以及磁带(ram),以便状态和当前磁带确定对磁带做什么.有关从图灵机到计算机的过渡,请参阅维基百科条目,图灵机等效项.
performance haskell lambda-calculus ghc higher-order-functions
我最近发布了一个问题,关于句法2.0有关的定义share
.我在GHC 7.6中有这个工作:
{-# LANGUAGE GADTs, TypeOperators, FlexibleContexts #-}
import Data.Syntactic
import Data.Syntactic.Sugar.BindingT
data Let a where
Let :: Let (a :-> (a -> b) :-> Full b)
share :: (Let :<: sup,
sup ~ Domain b, sup ~ Domain a,
Syntactic a, Syntactic b,
Syntactic (a -> b),
SyntacticN (a -> (a -> b) -> b)
fi)
=> a -> (a -> b) -> b
share = sugarSym Let
Run Code Online (Sandbox Code Playgroud)
但是,GHC 7.8希望-XAllowAmbiguousTypes …
核心是GHC的中间语言.阅读核心可以帮助您更好地了解您的计划的性能.有人问我关于阅读Core的文档或教程,但我找不到多少.
有哪些文档可供阅读GHC Core?
这是我到目前为止所发现的:
GHC有很多可以执行的优化,但我不知道它们是什么,也不知道它们在多大程度上被执行的可能性.
我的问题是:我可以期望每次或几乎可以应用哪些转换?如果我查看将要经常执行(评估)的一段代码,我的第一个想法是"嗯,也许我应该优化它",在这种情况下,我的第二个想法是,"甚至不要考虑它, GHC得到了这个"?
我正在阅读文章Stream Fusion:从列表到流到没有任何东西,以及他们用于将列表处理重写为不同形式的技术,GHC的正常优化将可靠地优化为简单的循环对我来说是新颖的.如何判断自己的程序何时符合这种优化条件?
GHC手册中有一些信息,但它只是回答问题的一部分.
编辑:我正在开始赏金.我想要的是一个低级转换列表,如lambda/let/case-floating,类型/构造函数/函数参数特化,严格性分析和拆箱,worker/wrapper,以及我遗漏的任何其他重要的GHC做的事情,以及输入和输出代码的解释和示例,以及理想情况下总效应大于其各部分之和的情况.理想情况下,有些人提到何时不会发生转变.我不期待对每个转换的新颖长度的解释,一些句子和内联单行代码示例就足够了(或者链接,如果它不是20页的科学论文),只要大图是在它结束时清楚.我希望能够查看一段代码,并能够很好地猜测它是否会编译成紧密循环,或者为什么不编译,或者我需要改变它来制作它.(我对像流融合这样的大优化框架(我只是阅读了一篇关于它的论文)感兴趣;更多的是那些编写这些框架的人所拥有的知识.)
我遇到了让GHC专门化一个具有类约束的函数的问题.我有我的问题就在这里的一个小例子:Foo.hs和 Main.hs.这两个文件编译(GHC 7.6.2,ghc -O3 Main
)并运行.
注意:
Foo.hs
真的被剥离了.如果您想了解为什么需要约束,可以在这里看到更多代码.如果我将代码放在一个文件中或进行许多其他微小的更改,GHC只是简单地将调用内联到plusFastCyc
.这不会发生在实际代码中,因为plusFastCyc
GHC内联太大,即使在标记时也是如此INLINE
.关键是要专门调用plusFastCyc
,而不是内联它.plusFastCyc
在真实代码的许多地方被调用,所以即使我可以强迫GHC这样做,也不可能复制这么大的功能.
感兴趣的代码是plusFastCyc
in Foo.hs
,在这里转载:
{-# INLINEABLE plusFastCyc #-}
{-# SPECIALIZE plusFastCyc ::
forall m . (Factored m Int) =>
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) ->
(FastCyc (VT U.Vector m) Int) #-}
-- Although the next specialization makes `fcTest` fast,
-- it isn't useful to me in …
Run Code Online (Sandbox Code Playgroud) 有一个简单的答案:为什么GHC这么大?
对于"如果Haskell是正确的工具,为什么我不应该关心大小"的传福音不感兴趣; 这是一个技术问题.
我正在尝试使用ghc-mod
vim插件进行类型/语法检查等.但是,我发现ghc-mod
总是在错误消息中使用完整的类型路径,例如:
test.hs|71 col 13 error| Couldn't match type ‘Data.Text.Internal.Text’
|| with ‘[GHC.Types.Char]’
|| Expected type: containers-0.5.6.2:Data.Map.Base.Map
|| [GHC.Types.Char]
|| ([(integer-gmp-1.0.0.0:GHC.Integer.Type.Integer,
|| integer-gmp-1.0.0.0:GHC.Integer.Type.Integer)],
|| containers-0.5.6.2:Data.Set.Base.Set
|| integer-gmp-1.0.0.0:GHC.Integer.Type.Integer)
|| Actual type: containers-0.5.6.2:Data.Map.Base.Map
|| Data.Text.Internal.Text
|| ([(integer-gmp-1.0.0.0:GHC.Integer.Type.Integer,
|| integer-gmp-1.0.0.0:GHC.Integer.Type.Integer)],
|| containers-0.5.6.2:Data.Set.Base.Set
|| integer-gmp-1.0.0.0:GHC.Integer.Type.Integer)
|| In the second argument of ‘containers-0.5.6.2:Data.Map.Base.map’, namely
|| ‘zippedMap’
|| In the second argument of ‘(GHC.Base.$)’, namely
|| ‘containers-0.5.6.2:Data.Map.Base.map
...
Run Code Online (Sandbox Code Playgroud)
这会使屏幕变得杂乱,我很难找出哪里出了问题.作为比较,这是使用以下内容的同一文件的错误消息ghci
:
test.hs:71:13:
Couldn't match type ‘T.Text’ with ‘[Char]’
Expected type: M.Map [Char] ([(Integer, …
Run Code Online (Sandbox Code Playgroud) ghc ×10
haskell ×10
performance ×2
constraints ×1
forall ×1
ghc-mod ×1
optimization ×1
syntax ×1
types ×1