JavaとCの実行速度

| | コメント(2)

自家製のプログラムを題材に、JavaとCの速度比較をしてみました。アクセスログからトップへのアクセスだけを切り出すという処理です。処理対象のファイルは某サイトの一ヶ月分のログで8.9Gあります。測定は、OSがLinuxバージョン2.6で、CPUがPentium Dの2.80GHz、メモリを512MB搭載しつつ、7,200RPMでキャッシュが8MBのSATA HDDというマシンで行なっています。

まずは、単純にgrepをかけてみましょう。

% time grep 'GET /[ ?]' access_log > /dev/null
grep 'GET /[ ?]' access_log > /dev/null  1219.31s user 7.65s system 99% cpu 20:33.59 total

さすがに8.9Gのファイルを相手にすると時間がかかります。

この処理をJavaで書いてみましょう。標準入力から1行読み込んで、トップページへのアクセスかどうかを正規表現を利用して調べています。もしトップページへのアクセスならば、標準出力にそのログの行を出力します。

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.regex.Pattern;

class MyGrep {
    private static final Pattern REGEXP = Pattern.compile("GET /[ ?]");

    public static void main(String[] args) throws Exception {
	String line;
	BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

	while ((line = reader.readLine()) != null) {
	    if(REGEXP.matcher(line).find())
		System.out.println(line);
	}
    }
}

実行結果はgrepよりだいぶ早くなりました。

% time java MyGrep < access_log  > /dev/null
java MyGrep < access_log > /dev/null  180.81s user 33.76s system 84% cpu 4:13.97 total

さらに最適化を試みてみましょう。Server VMを利用するために、-serverオプションを付加します。

% time java -server MyGrep < access_log  > /dev/null
java -server MyGrep < access_log > /dev/null  176.78s user 34.23s system 84% cpu 4:09.49 total

このプログラムでは、あまり効果はありませんでしたが、それでもオプションをひとつつけるだけで、4秒ほど早くなりました。 同様のプログラムをCで書いてみましょう。

#include <stdio.h>
#include <regex.h>

#define BUF_SIZE 256
#define PATTERN  "GET /[ ?]"

int main() {
  char buf[BUF_SIZE];
  FILE *fp;

  regex_t preg;
  regmatch_t pmatch[0];

  fp = fopen("/dev/stdin", "r");

  regcomp(&preg, PATTERN, REG_EXTENDED | REG_NOSUB);
    
  while(fgets(buf, BUF_SIZE, fp) != NULL) {
    if(regexec(&preg, buf, 0, pmatch, 0) == 0) {
      printf("%s", buf);
    }
  }

  regfree(&preg);
}
% time ./mygrep < access_log > /dev/null
./mygrep < access_log > /dev/null  48.60s user 6.29s system 27% cpu 3:20.47 total

やっぱりCは速いですね。Rubyで書いてもみました。

#!/usr/bin/ruby

REGEXP = /GET \/[ ?]/

STDIN.each do |l|
  print l if REGEXP === l 
end
% time ruby mygrep.rb < access_log > /dev/null
ruby mygrep.rb < access_log > /dev/null  95.02s user 7.98s system 49% cpu 3:26.06 total

うわ、このケースだとRubyってJavaよりも速い……。

コメント(2)

yone098 :

Cでregex.hじゃなくてstfing.hだけ(strcmp)使った場合はどうなんだろう

koichiro :

この例だとJavaでnio使ってもあまり変わらないかな?
8GBにMappedByteBufferは富豪すぎるかもしれませんが...

----MyGrep2.java----
import java.io.FileInputStream;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyGrep {
private static final Pattern LINE_REGXP = Pattern.compile(".*$", Pattern.MULTILINE);
private static final Pattern REGEXP = Pattern.compile("GET /[ ?]");


public static void main(String[] args) throws Exception {
FileInputStream in = new FileInputStream("access_log");
FileChannel channel = in.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
CharBuffer charBuffer = decoder.decode(buffer);
Matcher lineMatcher = LINE_REGXP.matcher(charBuffer);

while (lineMatcher.find()) {
String line = lineMatcher.group();
Matcher matcher = REGEXP.matcher(line);
if (matcher.find()) {
System.out.println(matcher.group());
}
}
in.close();
}
}

コメントする

著者について

高井直人
高井 直人
takai@recompile.net

ソフトウェアエンジニア。1977年横浜生まれ。大学在学中からネットワークや情報技術にたずさわる。Web制作会社などを経て、現在はシステムインテグレータに勤務。エンタープライズRubyをテーマに社内標準の策定などに従事している。

タグクラウド

ウェブページ

Powered by Movable Type 4.1-en-release-26-r1141-20080104