2012-04-22 73 views
8

我很好奇如何优化此代码:优化部分计算在Haskell

fun n = (sum l, f $ f0 l, g $ g0 l) 
    where l = map h [1..n] 

假设ff0gg0h都是昂贵的,但l的创建和存储是非常昂贵。

正如所写,l被存储,直到返回的元组被完全评估或垃圾收集。相反,length l,f0 lg0 l都应该在它们中的任何一个都被执行时执行,但fg应该被延迟。

看来这种行为可能是固定的写作:

fun n = a `seq` b `seq` c `seq` (a, f b, g c) 
    where 
    l = map h [1..n] 
    a = sum l 
    b = inline f0 $ l 
    c = inline g0 $ l 

还是很相似:

fun n = (a,b,c) `deepSeq` (a, f b, g c) 
    where ... 

我们或许可以指定一帮内部类型来达到相同的效果好,看起来很痛苦。还有其他选择吗?

另外,我明明跟我inline s表示编译器融合sumf0g0成一个单一循环,构建和消耗l逐项希望。我可以通过手动内联来明确这一点,但这很吸引人。有没有办法明确阻止创建和/或强制内联列表l?可能在编译期间内联或融合失败时会产生警告或错误的编译指示?

顺便说一句,我很好奇,为什么seqinlinelazy,等等,都由let x = x in x的序幕定义。这仅仅是为了给他们一个编译器的重写定义吗?

+4

回复最后一个问题:http://stackoverflow.com/a/8654407/1011995 – 2012-04-22 10:02:49

+0

'f0'和'g0'完全是任意的,还是可以用'foldr'来写? – dave4420 2012-04-22 10:38:49

+1

这里有没有足够的(a,b,c)-accumulator的简单折叠? – Sarah 2012-04-22 12:19:10

回答

3

如果您想确定,唯一的办法就是自己动手。对于任何给定的编译器版本,您可以尝试几个源代码公式,并检查生成的核心/程序集/ llvm字节代码/无论它是否按照您的要求进行。但是这可能会破坏每个新的编译器版本。

如果你写

fun n = a `seq` b `seq` c `seq` (a, f b, g c) 
    where 
    l = map h [1..n] 
    a = sum l 
    b = inline f0 $ l 
    c = inline g0 $ l 

deepseq其版本,编译器也许能合并的abc的计算并行(而不是在并发意义上)在单个进行遍历l,但暂时,我相信GHC不会,如果JHC或UHC做到了,我会感到惊讶。为此,计算结构bc需要足够简单。

在编译器和编译器版本中获得所需结果的唯一方法是自己做。在接下来的几年里,至少。

根据f0g0,这可能是因为这样做适当的蓄压式严格的左折叠和组合功能,如著名的平均

data P = P {-# UNPACK #-} !Int {-# UNPACK #-} !Double 

average :: [Double] -> Double 
average = ratio . foldl' count (P 0 0) 
    where 
    ratio (P n s) = s/fromIntegral n 
    count (P n s) x = P (n+1) (s+x) 

但如果f0和/或g0结构简单不适合,说一个左边的折叠,另一个是正确的折叠,可能不可能在一次遍历中进行计算。在这种情况下,选择是在重新创建l和存储l之间。存储l很容易通过显式共享来实现(where l = map h [1..n]),但如果编译器执行一些常见的子表达式消除操作(例如GHC确实倾向于共享该表单的列表,即使它没有CSE )。对于GHC,标记fno-cse-fno-full-laziness可以帮助避免不必要的共享。

+0

啊,有趣的一点是关于左侧和右侧的折叠!尽管我对你的CSE观点有些困惑。你是否简单地观察到CSE可以创建这个问题,当你试图对它进行天真的编码? – 2012-04-22 22:17:58

+0

如果重新创建列表比存储列表要便宜,您可以编写例如'f0(地图h [1..n])'和'g0(地图h [1..n])'。但编译器可能会消除公共子表达式'map h [1 .. n]'并在计算之间共享它。避免这种情况,如果它不合需要,就不如反面那么直截了当,共享一个子表达式(如果你将它绑定到一个名字上,'where l = map h [1..n]'),就完成了。基本上,是的,CSE可以引入这个问题,并且可能很难解决。 – 2012-04-22 22:24:25