Erlang需要多少年才算?

时间:2014-03-16 17:15:05

标签: java multithreading erlang

大家好我正在尝试制作一个负载生成器,我的目标是比较产生Erlang进程时产生的系统资源量与产生线程(Java)相比。我这样做是通过让程序计数到1000000000次。 Java需要大约35秒才能完成整个过程,创建了10个线程,Erlang需要花费10个进程,我对它不耐烦,因为它花了4分多钟计算。如果我只是让Erlang和Java计数到1000000000而不产生线程/进程,那么Erlang需要1分32秒而Java需要大约3秒钟。我知道Erlang并不是为了解决数字而做出的,但是差异很大,为什么会有这么大的差异呢?两者都使用我的CPU 100%但RAM没有峰值。我不确定可以使用哪些其他方法进行比较,我也对任何建议持开放态度。

这是两个版本的代码

-module(loop).

-compile(export_all).


start(NumberOfProcesses) ->
loop(0, NumberOfProcesses).

%%Processes to spawn
loop(A, NumberOfProcesses) -> 
if A < NumberOfProcesses -> 
    spawn(loop, outerCount, [0]),
    loop(A+1, NumberOfProcesses);   

    true -> ok
end.

%%outer loop 
outerCount(A) ->
if A < 10 ->

    innerCount(0),
    outerCount(A + 1);

    true -> ok
end.

%%inner loop 
innerCount(A) ->
if A < 1000000000 ->
    innerCount(A+1);

    true -> ok
end.

和java

import java.util.Scanner;

class Loop implements Runnable 
{

public static void main(String[] args) 
{

    System.out.println("Input number of processes");
    Scanner scan = new Scanner(System.in);        
    String theNumber = scan.nextLine();   


    for (int t = 0; t < Integer.parseInt(theNumber); t++)
    {
        new Thread(new Loop()).start();
    }
}

public void run() 
{
    int i;
    for (i = 0; i < 10; i++)
    {
        for (int j = 0; j < 1000000000; j++);
    }
}
}

3 个答案:

答案 0 :(得分:2)

您运行的是32位还是64位版本的Erlang?如果它是32位,则内循环限制1000000000不适合单字fixnum(最多28位,包括符号),循环将开始在堆上进行bignum算术,这是方式比增加一个单词和循环更昂贵(它也会导致垃圾收集时不时发生,以摆脱堆中旧的未使用的数字)。将外循环从10更改为1000并从内循环中相应地删除2个零应该使它甚至仅在32位BEAM上使用fixnum算法。

然后,它也是一个问题,即Java版本是否实际上正在做任何工作,或者是否在某些时候将循环优化为无操作。 (Erlang编译器没有做那种技巧 - 至少现在还没有。)

答案 1 :(得分:2)

RichardC的回答提供了一些了解执行时间差异的线索。我还要补充一点,如果编译了java代码,它可能会从微处理器的预测分支中受益匪浅,从而更好地利用缓存。

但我认为更重要的是,您没有选择合适的流程/处理比率来评估流程产生的成本。

测试使用10个过程来完成一些重要的工作。我会选择一个测试,其中产生了许多进程(数千个?我不知道JVM可以管理多少线程)每个进程只做很少的事情,例如这个代码在每一步产生两倍的进程数和等待最深的进程发回完消息。深度为17,这意味着总共有262143个进程和131072个返回消息,在我非常慢的PC上花费不到0.5秒,每个进程少于2μs(当然应该使用双核双线程)

-module (cascade).

-compile([export_all]).


test() ->
    timer:tc(?MODULE,start,[]).

start() ->
    spawn(?MODULE,child,[self(),17]),
    loop(1024*128).

loop(0) -> done;
loop(N) ->
    receive
        done -> loop(N-1)
    end.

child(P,0) -> P ! done;
child(P,N) ->
    spawn(?MODULE,child,[P,N-1]),
    spawn(?MODULE,child,[P,N-1]).

答案 2 :(得分:0)

这里有一些问题。

我不知道如何评估Java编译器正在做什么,但我还要下注它优化循环的存在。我认为你必须让循环做一些有意义的事情来进行任何比较。

更重要的是,Erlang代码并没有按照我的想法做到最好。看起来每个进程计数高达1000000000,然后再进行10次。

也许更糟糕的是,你的函数不是尾递归的,所以你的函数会在内存中累积,等待最后一个执行。 (编辑:我可能错了。不习惯if声明。)

这里的Erlang做了你想做的事。它仍然很慢。

-module(realloop).
-compile(export_all).

start(N) ->
    loop(0, N).

loop(N, N) ->
    io:format("Spawned ~B processes~n", [N]);
loop(A, N) ->
    spawn(realloop, count, [0, 1000000000]),
    loop(A+1, N).

count(Upper, Upper) ->
    io:format("Reached ~B~n", [Upper]);
count(Lower, Upper) ->
    count(Lower+1, Upper).