【发布时间】:2011-09-22 03:51:49
【问题描述】:
我对 Haskell/GHC 可以优化代码的程度不是很熟悉。下面我有一个 n 皇后问题的相当“蛮力”(在声明性意义上)实现。我知道它可以更有效地编写,但这不是我的问题。这让我开始思考 GHC 优化功能和限制。
我以我认为非常简单的声明式的方式表达了它。满足谓词 For all indices i,j s.t j<i, abs(vi - vj) != j-i 的 [1..n] 的过滤排列我希望这是可以优化的东西,但它也有点像询问很多编译器。
validQueens x = and [abs (x!!i - x!!j) /= j-i | i<-[0..length x - 2], j<-[i+1..length x - 1]]
queens n = filter validQueens (permutations [1..n])
oneThru x = [1..x]
pointlessQueens = filter validQueens . permutations . oneThru
main = do
n <- getLine
print $ pointlessQueens $ (read :: String -> Int) n
这运行相当缓慢并且增长很快。 n=10 大约需要一秒钟,n=12 需要永远。如果没有优化,我可以说增长是阶乘(排列数)乘以二次(要检查的谓词中的差异数)。有什么方法可以通过智能编译来更好地执行此代码?我尝试了基本的ghc 选项,例如-O2 并没有注意到明显的差异,但我不知道更精细的点(只是添加了 flagS)
我的印象是我调用的函数queens 无法优化,必须在过滤之前生成所有排列。免积分版有更好的机会吗?一方面,我觉得过滤器和谓词之间的智能函数理解可能能够在一些明显不受欢迎的元素完全生成之前将其剔除,但另一方面,这感觉有点儿问题。
对不起,如果这看起来很漫无边际,我想我的问题是
- 以上功能的pointfree版本是否更容易优化?
- 我可以在制作/编译/链接时采取哪些步骤来鼓励优化?
- 您能否简要描述一些可能的(并与不可能的对比!)优化上述代码的方法?这些发生在流程的哪个阶段?
-
ghc --make queensN -O2 -v输出中是否有我应该注意的特定部分?没有什么对我来说很突出。由于优化标志,甚至看不到输出有太大差异
我并不太关心这个代码示例,但我认为编写它让我思考,在我看来它是讨论优化的好工具。
PS - permutations 来自 Data.List ,看起来像这样:
permutations :: [a] -> [[a]]
permutations xs0 = xs0 : perms xs0 []
where
perms [] _ = []
perms (t:ts) is = foldr interleave (perms ts (t:is)) (permutations is)
where interleave xs r = let (_,zs) = interleave' id xs r in zs
interleave' _ [] r = (ts, r)
interleave' f (y:ys) r = let (us,zs) = interleave' (f . (y:)) ys r
in (y:us, f (t:y:us) : zs)
【问题讨论】:
-
无意义与否应该没有任何区别。一般来说,错误的算法是编译器无法修复的少数问题之一(相对简单的事情除外,例如将递归阶乘转换为特别聪明的编译器的循环)。
-
你选择了一个错误的算法,ghc 不会为你解决这个问题。
-
可能只是我,但我觉得这个问题的前提可能有点过于宽泛,无法以任何直接的方式回答:我目前将问题背后的驱动力解释为:什么可以在纯声明式函数代码上进行各种通用的、节省成本的全程序转换,这似乎是一个独立的研究领域,而且有点过于依赖给定的问题域。 @delnan 的观点非常尖锐;即使使用现代 智能 编译器,计算复杂性仍占主导地位。
-
我认为问题的前提是好的,但在我看来,最好将问题限制在编译器实际做了哪些优化,并提供一个可以接受的算法的代码示例高效的。再说一次,只是我的看法。
-
顺便说一句,
oneThru = enumFromTo 1.
标签: haskell ghc compiler-optimization