【发布时间】:2011-12-28 17:45:53
【问题描述】:
剧透警告:这与来自 Project Euler 的 Problem 14 有关。
以下代码运行大约需要 15 秒。我有一个在 1 秒内运行的非递归 Java 解决方案。我想我应该能够让这段代码更接近那个。
import Data.List
collatz a 1 = a
collatz a x
| even x = collatz (a + 1) (x `div` 2)
| otherwise = collatz (a + 1) (3 * x + 1)
main = do
print ((foldl1' max) . map (collatz 1) $ [1..1000000])
我使用+RHS -p 进行了分析,并注意到分配的内存很大,并且随着输入的增长而增长。对于n = 100,000,分配了1gb(!),对于n = 1,000,000,分配了13gb(!!)。
再一次,-sstderr 表明,虽然分配了很多字节,但总内存使用量为 1mb,效率为 95% 以上,所以可能 13gb 是红鲱鱼。
我能想到几种可能性:
有些事情并不像它需要的那样严格。我已经发现了
foldl1',但也许我需要做更多?是否可以标记collatz一样严格(这有意义吗?collatz不是尾调用优化。我认为应该是但不要 知道如何确认。编译器没有做一些我认为应该做的优化——例如 任何时候只有
collatz的两个结果需要在内存中(最大值和当前)
有什么建议吗?
这几乎是Why is this Haskell expression so slow? 的复制品,不过我会注意到快速Java 解决方案不需要执行任何记忆。有什么方法可以加快速度而不必求助于它?
作为参考,这是我的分析输出:
Wed Dec 28 09:33 2011 Time and Allocation Profiling Report (Final)
scratch +RTS -p -hc -RTS
total time = 5.12 secs (256 ticks @ 20 ms)
total alloc = 13,229,705,716 bytes (excludes profiling overheads)
COST CENTRE MODULE %time %alloc
collatz Main 99.6 99.4
individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 1 0 0.0 0.0 100.0 100.0
CAF Main 208 10 0.0 0.0 100.0 100.0
collatz Main 215 1 0.0 0.0 0.0 0.0
main Main 214 1 0.4 0.6 100.0 100.0
collatz Main 216 0 99.6 99.4 99.6 99.4
CAF GHC.IO.Handle.FD 145 2 0.0 0.0 0.0 0.0
CAF System.Posix.Internals 144 1 0.0 0.0 0.0 0.0
CAF GHC.Conc 128 1 0.0 0.0 0.0 0.0
CAF GHC.IO.Handle.Internals 119 1 0.0 0.0 0.0 0.0
CAF GHC.IO.Encoding.Iconv 113 5 0.0 0.0 0.0 0.0
和-sstderr:
./scratch +RTS -sstderr
525
21,085,474,908 bytes allocated in the heap
87,799,504 bytes copied during GC
9,420 bytes maximum residency (1 sample(s))
12,824 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Generation 0: 40219 collections, 0 parallel, 0.40s, 0.51s elapsed
Generation 1: 1 collections, 0 parallel, 0.00s, 0.00s elapsed
INIT time 0.00s ( 0.00s elapsed)
MUT time 35.38s ( 36.37s elapsed)
GC time 0.40s ( 0.51s elapsed)
RP time 0.00s ( 0.00s elapsed) PROF time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 35.79s ( 36.88s elapsed) %GC time 1.1% (1.4% elapsed) Alloc rate 595,897,095 bytes per MUT second
Productivity 98.9% of total user, 95.9% of total elapsed
还有 Java 解决方案(不是我的,取自 Project Euler 论坛,删除了记忆):
public class Collatz {
public int getChainLength( int n )
{
long num = n;
int count = 1;
while( num > 1 )
{
num = ( num%2 == 0 ) ? num >> 1 : 3*num+1;
count++;
}
return count;
}
public static void main(String[] args) {
Collatz obj = new Collatz();
long tic = System.currentTimeMillis();
int max = 0, len = 0, index = 0;
for( int i = 3; i < 1000000; i++ )
{
len = obj.getChainLength(i);
if( len > max )
{
max = len;
index = i;
}
}
long toc = System.currentTimeMillis();
System.out.println(toc-tic);
System.out.println( "Index: " + index + ", length = " + max );
}
}
【问题讨论】:
-
令人惊讶的是,GHC 没有像任何自尊的 C 编译器所期望的那样将 (quot n 2) 优化为 (rshift n 1)。有什么原因吗?
-
@solrize:我也很惊讶。