bookjovi 发表于 2013-2-3 14:16:16

Erlang/Java/Go threadring benchmark

shootout指通过一系列算法对language performance进行比较,具体可看:http://shootout.alioth.debian.org/
里面包含了很多算法:
 
# lsackermann       except          harmonic   knucleotide   message      nsievebits   raytracer   reversefile   tcpechoary             fannkuch      hash         LICENSE       messageringobjinst      README      robots.txt    tcprequestbinarytrees   fannkuchredux   hash2      license.txt   meteor       partialsumsrecursive   sieve         tcpsocketchameneos       fasta         health       lists         methcall   pidigits   regexdna    spectralnormtcpstreamchameneosreduxfastaredux      heapsort   magicsquaresmoments      primes       regexmatchspellcheck    threadringdiscovery       fibo            hello      mandelbrot    nbody      process      regexsub    strcat      wcdispatch      flyweightstateimplicitodematrix      nestedloop   prodcons   regsub      sumcol      wordfreqecho            gifdeanim       Include      matrixnorm    nsieve       random       revcomp   takfp 基本上通用的语言都包含了(这里也是比较各语言语法差异的一个好地方)
shootout官方测试的机器是ubuntu,kernel是3.0,arch包含x86 one-core/x86 quad-core/x86-64 one-core/x86-64 quad-core,每种arch下的测试结果排名上略有不同,但纵观所有测试结果,可以发现一些特点:
1)编译型语言往往比虚拟机语言速度更快,但也不完全是。如Java 7比Go在某些测试中速度更好(Javay有JIT)。
2)函数型语言综合排名一般都在中间,没有太差,也没有太好。
3)Fortran Intel是最快的,比C/C++更快,我想这是因为后面的Intel的缘故,Intel的c编译器比gcc也快。
4)P系列语言(Perl/Python/Ruby)往往是最慢的。我测试了一下nbody在Go,Java和Python的耗时,Go耗时1m,Python耗时46m,Java耗时最短40sec。
 
以上的结论是综合排名,在实际中影响测试结果的因素很多,同一种语言的不同算法实现也影响排名,
在threadring的测试,Erlang Hipe排名第一,haskell排名第二,Go排名第三。threadring算法其实很简单,这里就看看Go/Erlang/Java的threadring实现:
 
package mainimport (      "flag"      "fmt"      "os")var n = flag.Int("n", 1000, "how many passes")const Nthread = 503func f(i int, in <-chan int, out chan<- int) {      for {                n := <-in                if n == 0 {                        fmt.Printf("%d\n", i)                        os.Exit(0)                }                out <- n - 1      }}func main() {      flag.Parse()      one := make(chan int) // will be input to thread 1      var in, out chan int = nil, one      for i := 1; i <= Nthread-1; i++ {                in, out = out, make(chan int)                go f(i, in, out)      }      go f(Nthread, out, one)      one <- *n      <-make(chan int) // hang until ring completes} 在Go中thread用goroutine模拟,把503个thread组成一个环ring,向第一个thread中发送一个数字1000,每个thread收到数字减一后传递给下一个thread,直到数字减为0。
这个go程序耗时28sec(time ./8.out -n 50000000)
这里的go版本threadring并不是shootout官方版本,这里的是google的go实现中的版本,shootout版本在这里。
 
再看Erlang的threadring:
 
-module(threadring).-export().-define(RING, 503).start(Token) ->   H = lists:foldl(      fun(Id, Pid) -> spawn(threadring, roundtrip, ) end,      self(),      lists:seq(?RING, 2, -1)),   H ! Token,   roundtrip(1, H).roundtrip(Id, Pid) ->   receive      1 ->         io:fwrite("~b~n", ),         erlang:halt();      Token ->         Pid ! Token - 1,         roundtrip(Id, Pid)   end.main() ->   Token = list_to_integer(Arg),   start(Token).Erlang版本的threadring plain耗时17sec,比go版本还要少,erlang smp耗时就多到1m左右(由于threadring算法设计,导致在runqueue中只有一个running process,所以此场景下smp没有发挥优势,反而由于lock contention导致性能下降)。
 
 
# time erl -smp disable -noshell -run threadring main 50000000                                           292real    0m17.290suser    0m0.517ssys   0m16.768s# time erl -noshell -run threadring main 50000000             292real    1m9.036suser    0m4.266ssys   1m4.759s 下面是Java的threadring:
 
import java.util.LinkedList;import java.util.Queue;public class threadring {    public static void main(String[] args) {      Node[] ring = new Node;      for (int i=0; i<ring.length; i++) {            ring = new Node(i+1);      }      for (int i=0; i<ring.length; i++) {            int nextIndex = (ring.label % ring.length);            ring.next = ring;      }      int nHops = Integer.parseInt(args);      new Thread(new Consumer()).start();      ring.sendMessage(nHops);    }    private static Queue<Node> q = new LinkedList<Node>();    static class Consumer implements Runnable {      public void run() {            while (true) {                try {                  Node node;                  node = q.poll();                  if (node == null) {                        //ignore, wait for some element                        Thread.sleep(100);                  } else {                        node.run();                  }                } catch (InterruptedException e) {                  e.printStackTrace();                }            }      }    }    static class Node implements Runnable {      private final int label;      private Node next;      private int message;      public Node(int label) {            this.label = label;      }      public void sendMessage(int message) {            this.message=message;            q.add(this);      }      public void run() {            //                System.out.println("after lock");            if (message == 0) {                System.out.println(label);                System.exit(0);            } else {                next.sendMessage(message - 1);            }      }    }} 
这个Java版本的threadring实现过于简单,一个Consumer线程执行所有的Node,类似于Actor模式,所以此Java程序耗时极短,约2sec。
由于Java没有coroutine之类的设计,所以Java用真实的thread来模拟threadring更有说服力:
 
/** * The Computer Language Benchmarks Game * http://shootout.alioth.debian.org/ * contributed by Klaus Friedel */import java.util.concurrent.locks.LockSupport;public class threadring {static final int THREAD_COUNT = 503;public static class MessageThread extends Thread {    MessageThread nextThread;    volatile Integer message;    public MessageThread(MessageThread nextThread, int name) {      super(""+name);      this.nextThread = nextThread;    }    public void run() {      while(true) nextThread.enqueue(dequeue());    }    public void enqueue(Integer hopsRemaining) {      if(hopsRemaining == 0){      System.out.println(getName());      System.exit(0);      }      // as only one message populates the ring, it's impossible      // that queue is not empty      message = hopsRemaining - 1;      LockSupport.unpark(this); // work waiting...    }    private Integer dequeue(){      while(message == null){      LockSupport.park();      }      Integer msg = message;      message = null;      return msg;    }}public static void main(String args[]) throws Exception{    int hopCount = Integer.parseInt(args);    MessageThread first = null;    MessageThread last = null;    for (int i = THREAD_COUNT; i >= 1 ; i--) {      first = new MessageThread(first, i);      if(i == THREAD_COUNT) last = first;    }    // close the ring:    last.nextThread = first;    // start all Threads    MessageThread t = first;    do{      t.start();      t = t.nextThread;    }while(t != first);    // inject message    first.enqueue(hopCount);    first.join(); // wait for System.exit}} 这个Java程序则耗时非常长(time java -server threadring 50000000 )。
 
页: [1]
查看完整版本: Erlang/Java/Go threadring benchmark