GCJ Support

awk4j (AWK for Java Platform)

GCJ サポートとは

GCJ (GNU Compiler for Java) は、 Javaソースコードからネイティブな実行形式ファイルを生成するコンパイラで、 GCC (GNU Compiler Collection) の一部として開発されている。
コンパイルされたコードは、 Java環境に依存しない実行だけでなく、 Javaクラスの呼び出しも可能である。

実行環境について

AWKスクリプトを Javaソースに変換、 GCJ でネイティブマシンコードへコンパイルして実行する。

確認済みの動作環境は以下のとおり。

AWK言語 AWK コマンド (gawkなど)
トランスレータ Awk2Java (AWKスクリプトを Java言語に変換)
インタプリタ gij java
バイトコード コンパイラ gcj javac
ネイティブコード コンパイラ gcj (Ahead-of-Time Compiler) (java JIT Compiler)

コマンドリファレンス

Awk2Java コマンド (トランスレータ)

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 ネイティブコンパイラ)

gcj Javaソースコード名.java
    --main=Javaソースコード(クラス)名
    -o 実行形式ファイル名

    $(BIN)/libawk4j.a --classpath=$(LIB)/awk4j.jar   (ライブラリパス指定オプション)
    -O3 -Wl,--strip-all   (オ プティマイズオプション)

実行形式ファイルの実行 (AWKプログラム)

./実行形式ファイル名 [ 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 の導入

以下のコマンドを入力して 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スクリプトのコンパイルと実行

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

awk4j ライブラリの再構築

ソースコードからライブラリの再構築を行う場合には、 下記のコマンドを入力してライブラリをコンパイルする。

$ cd build
$ make clean install

Note: makefile, lib/include/* のファイルはプラットフォームのコードに変更する必要がある (導入時は UTF-8)
org/*/*.java のソースコードは UTF-8固定 (GCJの都合)

互換性について

現在判明している非互換点は以下のとおり。

文字セット: JISAutoDetect
日本語文字セットの自動判定をおこなう JISAutoDetect は実装されていない。
(ソケット通信などで、相手コードの自動判定ができると便利)

GCJ 関連リンク


GCJ Support
  1. GCJ とは
  2. 実行環境について
  3. コマンドリファレンス
    1. Awk2Java コマンド (トランスレータ)
    2. GCJ コマンド (Java ネイティブコンパイラ)
    3. 実行形式ファイルの実行 (AWKプログラム)
  4. benchmark
  5. インストールおよび環境設定
    1. GCJ の導入
    2. AWKスクリプトのコンパイルと実行
    3. awk4j ライブラリの再構築
  6. 互換性について
  7. GCJ 関連リンク