使用引用透明度来预计算 haskell 中的值



假设我们有一个这样的程序:

list = [1..10000000]
main = print $ sum list

我希望对其进行编译,以便可执行文件仅打印50000005000000,而无需花费太多时间和精力。

基本上,任何肯定会计算的表达式(也许严格性分析可以在这里有所帮助)都可以在编译时预先计算(即我们使用引用透明度来表示当我们计算值时并不重要)。

简而言之:"必须计算"+引用透明度=可以预先计算

这就像运行程序,直到我们达到依赖于输入的东西(即程序的核心,在所有输入中通用的,将被预先计算)

目前是否有现有的机制来实现这一目标(在Haskell或任何其他语言中)?[请不要在C++中指向类似模板的东西,因为它首先没有引用透明度。

如果没有,这个问题有多难?[随之而来的技术(和理论)问题是什么?

这在一般情况下是不安全的。原因是Haskell表达式可能是纯粹的,但它们也可能无法终止。编译器应始终终止,因此您能做的最好的事情就是"评估此结果的 1,000 个步骤"。1 但是,如果您确实添加了这样的限制,那么如果您正在编译一个程序以在具有 TB 级 RAM 的超级计算机集群上运行,并且编译器内存不足怎么办?

您可以添加很多限制,但最终您将优化减少为恒定折叠的缓慢形式(特别是对于大多数计算依赖于运行时用户输入的程序)。而且由于sum [1..10000000]在这里需要半秒钟,因此它不太可能受到任何合理的限制。

当然,像这样的特定情况通常可以优化

,而GHC通常确实可以优化像这样的复杂表达式。但是编译器不能只在编译时安全地计算任何表达式;它必须受到非常严格的约束,在这些约束下它会有多大帮助是有争议的。它是一个编译器,而不是解释器:)

1 这会大大减慢任何包含大量无限循环的程序的编译速度——由于 Haskell 是非严格的,这比你想象的更有可能)。或者,更常见的是,包含大量长时间运行的计算的任何程序。

进行编译时计算的通用答案是使用模板 Haskell。但是对于这个特定的用例,你可以使用矢量包和LLVM后端,GHC将优化这个总和。

sorghum:~% cat >test.hs
import Data.Vector.Unboxed as V
main = print (V.sum $ V.enumFromN (1 :: Int) 10000000)
sorghum:~% ghc --make -O2 -fllvm test.hs
[1 of 1] Compiling Main             ( test.hs, test.o )
Linking test ...
sorghum:~% time ./test
50000005000000
./test  0.00s user 0.00s system 0% cpu 0.002 total
sorghum:~% objdump -d test | grep 2d7988896b40
  40653d:   48 bb 40 6b 89 88 79    movabs $0x2d7988896b40,%rbx
  406547:   49 be 40 6b 89 88 79    movabs $0x2d7988896b40,%r14

(如果不是很明显,0x2d79888896b40 50000005000000

听起来像是超级编译的工作!这个问题听起来像是对它的描述,对非终止的讨论反映了超级编译器开发人员面临的问题。我在GHC维基上看到有人正在为它开发一个生产超级编译器,但不知道它变成了什么。

相关内容

  • 没有找到相关文章

最新更新