スクリプト言語の流儀〜C言語、Python、Ruby速度比較

投稿者: | 2016年2月9日

2年くらい前の記事だが、こんな記事が気になったので補足する。

俺の言語がこんなに遅いわけがない!? 〜C, Java, PHP, Python, Rubyによるプログラミング言語 速度比較〜

確かにスクリプト言語は遅い。ただし、C言語で一般的なアルゴリズムをそのまま使って計算したとしたら。

スクリプト言語にはスクリプト言語なりの流儀があり、慣れている人は違う方法で実装する。

ちょうどPythonについて、同じようなことを先日ソフトウェアジャパンというイベントでしゃべって来たのでこのスライドも参照されたい。

ここで改めてベンチマークをとってみたい。まずはPythonから。Pythonではこの手の数値計算をするのはNumpyというライブラリを使うのが普通である。Numpyで1からnが入った配列を用意し、そのメソッドsum()で合計を計算する。

結果の格納先になぜ辞書型を使うのかなど、気に入らないところもあるが、和の計算以外は上記リンク先のベンチマークコードをそのまま使う。

N = 10000
import numpy as np


def sumup(n):
    return np.arange(1, n + 1).sum()


def main():
    print("python with numpy start.")
    result = {}
    for count in range(1, N + 1):
        result[count - 1] = sumup(count)
    print("python with numpy end.")

main()

Pythonの数値計算でNumpyを使うのは充分に一般的だと思うが、Python本体に含まれてないライブラリを使って「Pythonのベンチマーク」と主張することに違和感を感じる人がいるかもしれないので、Numpyを使わない版も実験してみる。その場合、1からnが入ったリストを用意してsumという関数を適用するのが普通かと思う。

N = 10000


def sumup(n):
    return sum(range(1, n + 1))


def main():
    print("python without numpy start.")
    result = {}
    for count in range(1, N + 1):
        result[count - 1] = sumup(count)
    print("python without numpy end.")

main()

次にRubyで実験してみる。Rubyの配列にはinjectというメソッドがあるのでそれを利用してみる。

N = 10000

def sumup(n)
  return (1..n).inject{ |sum, x| sum + x }
end

def main()
  result = []
  puts "ruby start."
  (1..N).each do |count|
    result[count-1] = sumup(count)
  end
  puts "ruby end."
end

main()

C言語は、参照先ブログのコードをそのまま使い、最適化なし(オプション-O0)と最適化あり(-O3)を試してみる。

#include <stdio.h>
#define N 10000

long sumup(int n) {
  int i;
  long sum = 0;
  for (i=1; i<=n; i++) {
    sum += i;
  }
  return sum;
}

int main() {
  int count;
  long result[N];
  printf("c start.\n");
  for (count = 1; count <= N; count++) {
    result[count-1] = sumup(count);
  }
  printf("c end.\n");
  return 0;
}

参照先ブログにあるJavaとPHPはここでは無視する。

ベンチマーク結果

AWSのt2.microで、それぞれのバージョンは以下のとおり
OS: Ubuntu 14.04.3 LTS
Python: 3.5.1
Ruby: 1.9.3p484
gcc: 4.8.4

参照先にあるようにtimeコマンドで測定した。

 

言語 計算時間(msec)
Python(Numpyあり) 167
Python(Numpyなし) 739
Ruby 4159
C言語(最適化なし) 185
C言語(最適化あり) 1

考察

最適化付きのC言語が速すぎるのは、最適化しすぎて何もやっていないからである。アセンブリコードを吐かせて確認したが、main関数からsumup関数の呼び出し自体が消えている。なので、これと他を比べるのはあまり意味がないかと思う。

Python+Numpyは爆速であることがわかる。Numpyなしでもそれなりの速さは出ている。この結果だけをみるとRubyが遅いようだが、にしても参照先ブログからかなり速くなっており、受ける印象が随分違うと思う。

まとめ

  • C言語と同じような発想で同じアルゴリズムをそのまま実装するとスクリプト言語は非常に遅い
  • しかしスクリプト言語はスクリプト言語なりの流儀があり、慣れている人はどうすれば速くなるか知っている。そのコードはC言語とかけ離れることもある。

更新履歴:
2016/2/11 誤植の修正と若干の加筆

スクリプト言語の流儀〜C言語、Python、Ruby速度比較」への4件のフィードバック

  1. ピンバック: MF / Ruby/NArray遅くないよ

  2. undefined

    rubyに対して不平ですよ。

    def sumup(n)
    (1..n).sum
    end

    sumupのところにこうしたら爆速です。python with numpyより早いですよ。

    返信
    1. hamukazu 投稿作成者

      情報ありがとうございます。調べてみましたが、そのsumメソッドはRuby 2.4.0で導入されたようです。
      https://docs.ruby-lang.org/en/2.4.0/NEWS.html

      Ruby 2.4.0のリリースが2016年の12月25日ということですので、このブログよりあとで追加された機能のようです。

      返信

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です