mk-mode BLOG

このブログは自作の自宅サーバに構築した Debian GNU/Linux で運用しています。
PC・サーバ構築等の話題を中心に公開しております。(クローンサイト: GitHub Pages

ブログ開設日2009-01-05
サーバ連続稼働時間
Reading...
Page View 合計
Reading...
今日
Reading...
昨日
Reading...

ベンチマーク - C, C++, Objective-C, Java, Scala, Ruby, Python!

[ プログラミング ] [ C言語, Java, Python, Ruby, Scala ]

こんばんは。

各種プログラミング言語、 C, C++, Objective-C, Java, Scala, Ruby, Python でベンチマークテストを行ってみました。

0. 前提条件

  • Linux Mint 17(64bit) での作業を想定。
  • メソッド呼び出しを 231 - 1 (= 2,147,483,647) 回 行う。
  • 使用した言語のバージョン
    • GCC 4.8.2 (パッケージインストール)
    • GCC 4.9.1 (ソースビルドインストール)
    • Oracle Java 1.8.0_05 (アーカイブインストール)
    • Scala 2.11.2 (アーカイブインストール)
    • Ruby 1.9.3-p484 (パッケージインストール)
    • Ruby 2.1.3-p242 (ソースビルドインストール)
    • Python 2.7.6 (パッケージインストール)
    • Python 3.4.0 (ソースビルドインストール)

1. 作成ソースコード・スクリプト

A benchmark to estimate time to invoke a method” のコード・スクリプトを参考にした。

1-1. C 言語

BenchC.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/time.h>

typedef struct {
    int n0;
} Bench;

int calc(Bench *s, int n){
    int n1 = s->n0 + (1 - 2 * (n % 2));
    s->n0 = n;
    return n1;
}

double gettimeofday_sec()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + (double)tv.tv_usec * 1e-6;
}

int main(){
    // Declaration
    unsigned int i;
    int          n = 1;
    double       t1, t2;

    // Instantiation
    Bench *objBench = (Bench *)malloc(sizeof(Bench));
    objBench->n0 = 0;

    // Main process
    t1 = gettimeofday_sec();
    for (i = 0; i < INT_MAX; i++) {
        n = calc(objBench, n);
    }
    t2 = gettimeofday_sec();
    printf("C\t-> %8.4f secs.\n", t2 - t1);

    // Finish
    free(objBench);
    return 0;
}

コンパイル。

1
$ gcc -Wall -O2 -o BenchC BenchC.c

1-2. C++

BenchCpp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include <iostream>
#include <limits.h>
#include <sys/time.h>

using namespace std;

class Bench {
public:
    Bench();
    int calc(int n);
private :
    int n0;
};

Bench::Bench(){
    n0 = 0;
}

int Bench::calc(int n){
    int n1 = n0 + (1 - 2 * (n % 2));
    n0 = n;
    return n1;
}

double gettimeofday_sec()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + (double)tv.tv_usec * 1e-6;
}

int main(){
    // Declaration
    unsigned int i;
    int          n = 1;
    double       t1, t2;

    // Instantiation
    Bench *objBench = new Bench();

    // Main process
    t1 = gettimeofday_sec();
    for (i = 0; i < INT_MAX; i++) {
        n = objBench->calc(n);
    }
    t2 = gettimeofday_sec();
    printf("C++\t-> %8.4f secs.\n", t2 - t1);

    // Finish
    delete objBench;
    return 0;
}

コンパイル。

1
$ g++ -Wall -O2 -o BenchCpp BenchCpp.cpp

1-3. Objective-C

BenchM.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#import <stdio.h>
#import <Foundation/NSObject.h>
#import <limits.h>
#import <sys/time.h>

@interface Bench : NSObject {
    int n0;
}
- (id)init;
- (int)calc:(int)n;
@end

@implementation Bench
- (id)init{
    [super init];
    n0 = 0;
    return self;
}
- (int)calc:(int)n {
    int n1 = n0 + (1 - 2 * (n % 2));
    n0 = n;
    return n1;
}
@end

double gettimeofday_sec()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + (double)tv.tv_usec * 1e-6;
}

int main(){
    // Declaration
    unsigned int i;
    int          n = 1;
    double       t1, t2;

    // Instantiation
    id objBench = [[Bench alloc] init];

    // Main process
    t1 = gettimeofday_sec();
    for(i = 0; i < INT_MAX; i++){
        n = [objBench calc:n];
    }
    t2 = gettimeofday_sec();
    printf("Obj-C\t-> %8.4f secs.\n", t2 - t1);

    // Finish
    return 0;
}

コンパイル。

1
$ gcc -Wall -O2 -o BenchM BenchM.m -lobjc -lgnustep-base -I/usr/include/GNUstep

1-4. Java

BenchJava.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Bench {
    int n0;

    Bench() {
        n0 = 0;
    }

    int calc(int n) {
        int n1 = n0 + (1 - 2 * (n % 2));
        n0 = n;
        return n1;
    }
}

class BenchJava {
    public static void main(String[] args) {
        // Declaration
        int  n = 1;
        long t1, t2;

        // Instantiation
        Bench objBench = new Bench();

        // Main process
        t1 = System.currentTimeMillis();
        for(int i = 0; i < Integer.MAX_VALUE; i++) {
            n = objBench.calc(n);
        }
        t2 = System.currentTimeMillis();
        System.out.printf("Java\t-> %8.4f secs.\n", (float)(t2 - t1)/1000);
    }
}

コンパイル。

1
$ javac BenchJava.java

1-5. Scala

BenchScala.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Bench {
    def calc(n:Int): Int = {
        var n1 = n0 + (1 - 2 * (n % 2))
        n0 = n;
        return n1;
    }
    private var n0:Int = 0
}

object BenchScala {
    def main(args :Array[String]):Unit = {
        // Declaration
        var n = 1

        // Instantiation
        var o = new Bench

        // Main process
        var t1 = System.currentTimeMillis
        for(i <- 0 to Integer.MAX_VALUE - 1){
            n = o.calc(n);
        }
        var t2 = System.currentTimeMillis
        printf("scala\t-> %8.4f secs.\n", (t2 - t1) / 1000.0)
    }
}

コンパイル。

1
$ scalac BenchScala.scala

1-6. Ruby

以下は ruby が “/usr/bin/ruby” にある場合の例。(インストール状況によりパスを変更すること。 “/usr/bin/env ruby” にしたり、シェバングを使用せずに ruby コマンドで当スクリプトを指定して実行してもよい)

bench_ruby.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#!/usr/bin/ruby
class Bench
  def initialize
    @n0 = 0
  end

  def calc(n)
    n1 = @n0 + (1 - 2 * (n % 2))
    @n0 = n
    n1
  end
end

if __FILE__ == $0
  # Declaretion
  i = 0
  n = 1

  # Instantiation
  obj_bench = Bench.new

  # Main Process
  t1 = Time.now
  while i < 2147483647
    n = obj_bench.calc(n)
    i += 1
  end
  t2 = Time.now
  printf("Ruby\t-> %8.4f secs.\n", t2 - t1)
end

1-7. Python

以下は python が “/usr/bin/pyhon” にある場合の例。(インストール状況によりパスを変更すること。 “/usr/bin/env python” にしたり、シェバングを使用せずに python コマンドで当スクリプトを指定して実行してもよい)

bench_python.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python
import time

class Bench:
  def __init__(self):
    self.n0 = 0

  def calc(self, n):
    n1 = self.n0 + (1 - 2 * (n % 2))
    self.n0 = n
    return n1

if __name__ == "__main__":
  # Declaration
  i = 0
  n = 1

  # Instantiation
  obj_bench = Bench()

  # Main Process
  t1 = time.time()
  while i < 2147483647
    n = obj_bench.calc(n)
    i += 1
  t2 = time.time()
  print("Python\t-> %8.4f secs." %(t2 - t1))

2. 実行

それぞれ何回か実行した平均的な値。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ ./BenchC
C       ->   4.9577 secs.        # <= GCC 4.8.2

$ ./BenchC
C       ->   0.7050 secs.        # <= GCC 4.9.1

$ ./BenchCpp
C++     ->   5.0470 secs.        # <= G++ 4.8.2

$ ./BenchCpp
C++     ->   4.9888 secs.        # <= G++ 4.9.1

$ ./BenchM
Obj-C   ->  14.9532 secs.        # <= GCC 4.8.2

$ ./BenchM
Obj-C   ->  14.8954 secs.        # <= GCC 4.9.1

$ java BenchJava
Java    ->   6.8070 secs.        # <= Java 1.8.0_05

$ scala -cp . BenchScala
scala   ->  10.0480 secs.        # <= Scala 2.11.2

-> % ./bench_ruby.rb
Ruby    -> 309.2438 secs.        # <= Ruby 1.9.3-p484

$ ./bench_ruby.rb
Ruby    -> 255.8539 secs.        # <= Ruby 2.1.3-p242

$ ./bench_python.py
Python  -> 920.2962 secs.        # <= Python 2.7.6

$ ./bench_python.py
Python  -> 1070.3830 secs.       # <= Python 3.4.0

3. 所感等

  • C < C++ < Java < Scala < Objective-C <<< Ruby < Python という結果となった。
  • GCC(G++) は 4.8.2 より 4.9.1 の方が高速である。(C に関しては大幅に高速化される)
  • Objective-C は C, C++ に比べるとかなり遅い。
  • Objective-C は Java, Scala よりも遅い。
  • 当然ながら、Ruby, Python のスクリプト言語はかなり遅い。(特に Python はかなり遅い)
  • Python はパッケージでインストールした 2.7.6 の方が、ソースをビルドしてインストールした 3.4.0 より若干高速。
  • 例えば、Ruby は C++ の約50倍遅いわけだが、言い換えれば C++ で 1ms で終わる処理が Ruby では 50ms かかるということであり、日常生活ではそれほど困ることはないだろう。(大きな処理でなければ)
  • 今回はあくまでも単純なメソッド呼び出しだったが、処理よっては速度も異なるだろう。
    言語により得手不得手があるので、一概に評価はできない、ということ。

参考サイト


機会があれば他の言語でもベンチマークテストを行ってみてもよいかも知れない。

以上。

Comments