Things to Feel Fun

見て感じる楽しいこと

Crystalはどれくらい速いのか

Ruby風の文法で、ネイティブコンパイルできる言語Crystal

実際に使ってみましたが、凄そうな予感がします。そこで、実際にパフォーマンスを計測することにしました。実行環境は以下の通りです。

測定方法は竹内関数で、コードは後記します。

測定する言語

補足

コンパイラのバージョン

$ gcc -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer//usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

$ g++ -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer//usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin13.4.0
Thread model: posix

$ crystal -v
Crystal 0.7.6 [eb13f75] (Thu Aug 13 22:00:15 UTC 2015)

$ java -version
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
java version "1.8.0_05"
Java(TM) SE Runtime Environment (build 1.8.0_05-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.5-b02, mixed mode)

$ ruby -v
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin13]

測定結果

コードはこちら

  • C:  7062 ms
  • C++:  7137 ms
  • Crystal:  6278 ms
  • Java:  4705 ms
  • Ruby:  105440 ms (105秒)

f:id:tekt:20150901144111p:plain

たらい回し関数の実行時間を示しています。短い方がパフォーマンスが良いということです。

CrystalとRubyの実行速度の違いは歴然ですね。CrystalはCと同等の速さを示しています。JavaとCrystalが妙に速いのは、末尾再帰最適化とGCのためだと思われます。

コード

C

#include <stdio.h>

#ifdef WIN32

#include <windows.h>
double get_time()
{
    LARGE_INTEGER t, f;
    QueryPerformanceCounter(&t);
    QueryPerformanceFrequency(&f);
    return (double)t.QuadPart/(double)f.QuadPart;
}

#else

#include <sys/time.h>
#include <sys/resource.h>

double get_time()
{
    struct timeval t;
    struct timezone tzp;
    gettimeofday(&t, &tzp);
    return t.tv_sec + t.tv_usec*1e-6;
}

#endif

// ==========================

int tarai(int x,int y,int z){
  if(x <= y){
    return y;
  }else{
    return tarai(tarai(x-1,y,z), tarai(y-1,z,x), tarai(z-1,x,y));
  }
}

int main() {
  double t1 = get_time();
  int n = tarai(14,8,0);
  double t2 = get_time();

  printf("%d\n", n);
  printf("%f ms\n", (t2 - t1) * 1000.0);
}

C++

#include <iostream>

#ifdef WIN32

#include <windows.h>
double get_time()
{
    LARGE_INTEGER t, f;
    QueryPerformanceCounter(&t);
    QueryPerformanceFrequency(&f);
    return (double)t.QuadPart/(double)f.QuadPart;
}

#else

#include <sys/time.h>
#include <sys/resource.h>

double get_time()
{
    struct timeval t;
    struct timezone tzp;
    gettimeofday(&t, &tzp);
    return t.tv_sec + t.tv_usec*1e-6;
}

#endif

// ==========================

int tarai(int x,int y,int z){
  if(x <= y){
    return y;
  }else{
    return tarai(tarai(x-1,y,z), tarai(y-1,z,x), tarai(z-1,x,y));
  }
}

int main() {
  double t1 = get_time();
  int n = tarai(14,8,0);
  double t2 = get_time();

  std::cout << n << std::endl;
  std::cout << (t2 - t1) * 1000.0 << " ms" << std::endl;
}

Crystal, Ruby

def tarai(x,y,z)
  if x <= y
    y
  else
    tarai(tarai(x-1,y,z), tarai(y-1,z,x), tarai(z-1,x,y))
  end
end

t1 = Time.now
n = tarai(14,8,0)
t2 = Time.now
puts n
puts t2 - t1

Java

public class Tak {
  public static int tarai(int x,int y,int z){
    if(x <= y){
      return y;
    }else{
      return tarai(tarai(x-1,y,z), tarai(y-1,z,x), tarai(z-1,x,y));
    }
  }

  public static void main(String[] args) {
    long t1 = System.currentTimeMillis();
    int n = tarai(14,8,0);
    long t2 = System.currentTimeMillis();
    
    System.out.println(n);
    System.out.println((t2 - t1) + " ms");
  }

}