GCJ Support
awk4j
(AWK for Java Platform)
GCJ
(GNU Compiler for Java) は、
Javaソースコードからネイティブな実行形式ファイルを生成するコンパイラで、
GCC (GNU Compiler Collection) の一部として開発されている。
コンパイルされたコードは、
Java環境に依存しない実行だけでなく、 Javaクラスの呼び出しも可能である。
AWKスクリプトを Javaソースに変換、
GCJ でネイティブマシンコードへコンパイルして実行する。
確認済みの動作環境は以下のとおり。
- OS : Linux, Ubuntu 9.04
- GCJ : 4.3.3
- Java : Sun Java 1.6.0_13
- Make : GNU Make 3.81
- CPU : Intel Core 2 Duo (1.83GHz), Memory : 2 MByte
AWK言語 |
AWK コマンド (gawkなど) |
トランスレータ |
Awk2Java
(AWKスクリプトを Java言語に変換) |
インタプリタ |
gij |
java |
バイトコード コンパイラ |
gcj |
javac |
ネイティブコード コンパイラ |
gcj (Ahead-of-Time Compiler) |
(java JIT Compiler) |
Awk2Java.exe [ options ] AWKスクリプト名
(クラス名として使用するため英大文字で始まる英数字を推奨)
or
Awk2Java.exe [ options ] -srcname クラス名 --source 'AWKスクリプト'
主なオプション
-include include-dir
インクルードライブラリのディレクトリ ("../lib/include")
-package package-name 出力パッケージ名 ("")
-srcname
sorce-name クラス名 ("Script")
-d output-dir
Javaソースコード出力ディレクトリ ("")
or
> output-script-file
リダイレクトで出力
gcj Javaソースコード名.java
--main=Javaソースコード(クラス)名
-o 実行形式ファイル名
$(BIN)/libawk4j.a
--classpath=$(LIB)/awk4j.jar
(ライブラリパス指定オプション)
-O3 -Wl,--strip-all (オ
プティマイズオプション)
./実行形式ファイル名 [ options ] [ コマンドパラメータ ]
主なオプション
(AWKコマンドオプション)
-F fs
--field-separato fs
組み込み変数 FS (入力フィールドセパレータ) に fs を設定する
-v var=val
--assign
var=val スクリプトを実行する前に、変数 var に値
val を設定する
--
オプションの終了を意味し、AWKアプリに "-" で始まる引数を与える場合に使用する
環境変数
CLASSPATH など
Note: コマンドの使用方法および環境変数の設定方法などについてはサンプル
( makefile )を参照
ベンチマークプログラムとして、エラトステネスのふるいを使用して 100,000 までの素数を求める。
メモリを食い潰さないように作業配列の最大要素数を 10,000 として、大量のオブジェクト生成と廃棄が発生する。
このため処理系のオブジェクト および メモリ管理特性が顕著となるとおもわれる。
cd gcj
make era
$ time gawk -f Eratosthenes.awk 100000
$ time java -server -cp .:../lib/awk4j.jar
Eratosthenes 100000
$ time ./Eratosthenes.exe 100000
$ time gij --cp .:../lib/awk4j.jar
Eratosthenes 100000
Note: 基本命令 (比較、四則演算など) と連想配列アクセス
が大量に発生するサンプル。
なんと、 2 回目以降の起動では、 awk4j + Java が GNU AWK の性能を上回っている (JavaVM JIT
Compiler
に脱帽)。
Java、 gcj 共に想定以上の性能を叩き出している。
« サンプル 1 »
実行環境 |
elapsed |
system |
相対性能 |
gawk |
00:02.60 |
0.00 |
1.0 |
java |
00:01.90 |
0.12 |
0.7 |
gcj |
00:03.92 |
0.08 |
1.5 |
gij |
00:52.56 |
0.13 |
20.2 |
次に、サンプル 1
と同等な処理になるよう Java に書き直した
(awk4jライブラリを使用しない)
コードの結果を示す。
« サンプル 2 »
実行環境 |
elapsed |
system |
相対性能 |
gcj |
00:00.52 |
0.06 |
1.0 |
java |
00:00.72 |
0.12 |
1.4 |
gij |
00:01.18 |
0.05 |
2.3 |
Note: Java SDK
クラスのみ使用した場合には
(awk4j ライブラリを使用しない) gcj の事前コンパイルの効果が顕著で、
gcj が Java の性能を上回っている。
では、一般的な AWKスクリプトを実行した場合の awk4j
性能はどうか?
awk4j のAWK構文解析部を DOT言語(Graphviz) にて
グラフ化で使用したサンプル (Graph.awk)
を実行した場合の結果を示す。
cd gcj
make graph
$ time gawk -f src/Graph.awk ../lib/script/Awk2Java.awk
$ time java -server -cp .:../lib/awk4j.jar Graph
../lib/script/Awk2Java.awk
$ time ./Graph.exe ../lib/script/Awk2Java.awk
« サンプル 3 »
実行環境 |
elapsed |
system |
相対性能 |
gawk |
00:00.18 |
0.00 |
1.0 |
java |
00:01.64 |
0.07 |
9.1 |
gcj |
00:09.22 |
0.04 |
51.2 |
Note: 正規表現、連想配列、ストリーム入力、欄への分解など
AWKらしい文字列操作を中心とした処理であり、
awk4j での
AWK言語仕様エミュレーション負荷は高い、
一般的には
10 倍ほど遅いと思えばよい。
« サンプル 1 »
##
Eratosthenes: エラトステネスのふるい(AWK版)
BEGIN {
maxarray = 10000;
# 作業配列サイズ
maxprime = ARGV[1];
#
求める素数の最大値
for (base = 2; base <= maxprime;
base += maxarray) {
arraysize = (maxarray < maxprime - base + 1) ? maxarray :
maxprime - base + 1;
delete array;
for (i = 2; i < (base + arraysize) / 2; i++) {
if (base <= i)
k = i + i;
else if (0 == base % i)
k = base;
else
k = int(base / i + 1) * i;
for (; k < base + arraysize; k += i)
array[k - base] = "";
}
for (i = 0; i < arraysize; i++) {
if (!(i in array))
print base + i;
# 素数を出力
}
}
}
上記の AWKスクリプトと同等な処理となるよう Java で書き直したコード。
« サンプル 2 »
//
Eratosthenes2: エラトステネスのふるい(Java版)
public class Eratosthenes2 {
public static void main(String[] args) {
int maxarray = 10000;
// 作業配列サイズ
int maxprime = Integer.parseInt(args[0]);
// 求める素数の最大値
java.util.HashMap<String, String> array = new
java.util.HashMap<String, String>();
for (int base = 2; base <= maxprime; base += maxarray) {
int arraysize = Math.min(maxarray, maxprime - base + 1);
array.clear();
for (int i = 2; i < (base + arraysize) / 2; i++) {
int k;
if (base <= i) {
k = i + i;
} else if (0 == base % i) {
k = base;
} else {
k = (base / i + 1) * i;
}
for (; k < base + arraysize; k += i) {
array.put(String.valueOf(k - base), "");
}
}
for (int i = 0; i < arraysize; i++) {
if (!array.containsKey(String.valueOf(i))) {
System.out.println(base + i);
// 素数を出力
}
}
}
}
}
Note: awk4j と同様の処理とするために作業配列を
java.util.HashMap としている。
性能的には java.util.BitSet の使用がベスト。 (詳細はサンプルソースを参照)
以下のコマンドを入力して gcj を導入する。
$ sudo apt-get install gcj
Java 6 の導入 (オプション)
$ sudo aptitude install sun-java6-bin
sun-java6-fonts sun-java6-jre sun-java6-jdk sun-java6-plugin
GNU AWK の導入 (オプション)
$ sudo apt-get install gawk
AWKスクリプトのコンパイルと実行については、 サンプルmakeファイルを参照。
$ cd gcj
$ make
« ディレクトリ構成 »
awk4j-gcj
# awk4j-gcj を展開したディレクトリ
+-- lib
+-- include # コンパイルに使用
awk4j.jar # コンパイル・リンクおよびインタプリタ実行時に使用
+-- build
+-- obj #
リンクライブラリ構築のための作業用ディレクトリ (.o)
+-- org # awk4jライブラリ
Javaソース (ソースリリースと同じもの)
+-- resources # awk4j実行時リソース
makefile
# awk4jライブラリをビルドするための makeファイル
+-- gcj
+-- bin
# コンパイル結果の、トランスレータ(Awk2Java.exe) と リンクライブラリ(libawk4j.a)
+-- resources # awk4j実行時リソース
+-- src
# サンプルソース
makefile
# gcjサンプル makeファイル
Note: makefile, lib/include/*,
src/*
のファイルはプラットフォームのコードに変更する必要がある (導入時は UTF-8)
また、bin/*.exe には実行権の付与が必要 chmod +x bin/*.exe
ソースコードからライブラリの再構築を行う場合には、
下記のコマンドを入力してライブラリをコンパイルする。
$ cd build
$ make clean install
Note: makefile, lib/include/*
のファイルはプラットフォームのコードに変更する必要がある (導入時は UTF-8)
org/*/*.java のソースコードは UTF-8固定 (GCJの都合)
現在判明している非互換点は以下のとおり。
- 文字セット: JISAutoDetect
- 日本語文字セットの自動判定をおこなう JISAutoDetect は実装されていない。
(ソケット通信などで、相手コードの自動判定ができると便利)