【问题标题】:Haskell program runs very slowHaskell 程序运行很慢
【发布时间】:2019-03-07 13:24:39
【问题描述】:

我编写了我的第一个计算素数的程序。但是它运行得很慢,我不知道为什么。我在 java 中编写了类似的代码,对于 n = 10000,java 程序不需要任何时间,而 Haskell 程序需要 2 分钟。

import Data.List
main = do
    print "HowManyPrimes? - OnlyInteger"
    inputNumber <- getLine
    let x = (read inputNumber :: Int)
    print (firstNPrimes x)

-- prime - algorithm
primeNumber:: Int -> Bool
primeNumber 2 = True
primeNumber x = primNumberRec x (div x 2)

primNumberRec:: Int -> Int -> Bool
primNumberRec x y
      |y == 0 = False
      |y == 1 = True 
      |mod x y == 0 = False
      |otherwise = primNumberRec x (y-1)

-- prime numbers till n
primesTillN:: Int -> [Int]
primesTillN n = 2:[ x | x <- [3,5..n], primeNumber x ]


--firstNPrimes
firstNPrimes:: Int -> [Int]
firstNPrimes 0 = []
firstNPrimes n = 2: take (n-1) [x|x <- [3,5..], primeNumber x]

提前致谢。

类似的java代码:

import java.util.Scanner;
public class PrimeNumbers{

static Scanner scan = new Scanner(System.in);

public boolean primeAlgorithm(int x){
    if (x < 2)
        return false;
    return primeAlgorithm(x, (int)Math.sqrt(x));
}

public boolean primeAlgorithm(int x, int divider){
    if (divider == 1)
        return true;
    if (x%divider == 0)
        return false;
    return primeAlgorithm(x, divider-1);
}

public static void main(String[] args){

    PrimeNumbers p = new PrimeNumbers();
    int howManyPrimes = scan.nextInt();
    int number = 3;
    while(howManyPrimes!=0){
        if(p.primeAlgorithm(number)){
            System.out.print(number+" ");
            howManyPrimes--;
        }
        number+=2;
    }

}

}

【问题讨论】:

  • 无法重现。 ghc -O2 test &amp;&amp; echo 10000 | /usr/bin/time ./test 说这在我几年前的电脑上花了 11 秒。你是如何得到这些计时数字的?您要比较的 Java 代码是什么?
  • (举一个我为什么要 Java 代码的例子,我敢打赌你的 for(i = 2; i &lt;= n/2; i++) 里面有类似 for(i = 2; i &lt;= n/2; i++) 的东西——这不是 与您在这里使用的算法相同,倒计时而不是倒计时!仅在我的机器上进行的更改就将时间从 11 秒缩短到 3 秒。)
  • 我提供了java代码。我在 java 中使用了 sqrt 函数,但我无法在 haskell 中应用它,但是除以 2 后,java 程序仍然非常快。
  • 可能是我机器的原因?我正在使用 haskell plattformer ghci.exe 运行 haskell 代码,使用 cmd 运行 java 程序。

标签: performance haskell functional-programming


【解决方案1】:

进行时序测量时,始终编译; ghci 设计用于快速更改-重建-运行循环,而不是用于快速执行生成的代码。但是,即使遵循此建议,您的两个 sn-ps 之间仍存在巨大的时间差异。

您的 java 和 Haskell 之间的主要区别是使用 sqrt 而不是除以 2。您的原件,在我的机器上:

% javac Test.java && echo 10000 | /usr/bin/time java Test >/dev/null                             
0.21user 0.02system 0:00.13elapsed 186%CPU (0avgtext+0avgdata 38584maxresident)k
0inputs+0outputs (0major+5823minor)pagefaults 0swaps
% ghc -O2 test && echo 10000 | /usr/bin/time ./test >/dev/null
8.85user 0.00system 0:08.87elapsed 99%CPU (0avgtext+0avgdata 4668maxresident)k
0inputs+0outputs (0major+430minor)pagefaults 0swaps

Java 为 0.2 秒,Haskell 为 8.9 秒。切换到使用平方根后进行以下更改:

- primeNumber x = primNumberRec x (div x 2)
+ primeNumber x = primNumberRec x (ceiling (sqrt (fromIntegral x)))

我得到了 Haskell 的以下时间:

% ghc -O2 test && echo 10000 | /usr/bin/time ./test >/dev/null
0.07user 0.00system 0:00.07elapsed 98%CPU (0avgtext+0avgdata 4560maxresident)k
0inputs+0outputs (0major+427minor)pagefaults 0swaps

现在比 java 代码快 3 倍。 (当然还有更好的算法可以让它更快。)

【讨论】:

    【解决方案2】:

    编译它!

    GHCi 中的 Haskell 代码远未优化;尝试使用ghc -o prime prime.hs 将其编译成二进制文件,甚至更好地使用-O2 优化。我曾经有一个脚本在 GHCi 中需要 5 分钟,但编译后只需几秒钟。

    【讨论】:

    • 这有帮助。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-16
    • 1970-01-01
    • 2017-01-28
    相关资源
    最近更新 更多