2004年1月の技術日記


Jan.1,2004 (Thu)

あけましておめでとうございます。
今年も出来る限り精一杯がんばりたいと思います。

Jan.4,2004 (Sun)

脆弱性報告と〜の壁

「ネットの脆弱さに警鐘」国立大研究員が個人情報を公表(by 朝日)

事の顛末自体は11月に出ていた。今頃になって1面に出たのは何かの意図があるのか。とりあえず、今回のoffice氏のやり方がまずかったのは事実。不正アクセスをしたというよりは、かなり機密性の高い個人情報を流してしまったことがまずかった。この問題は/.Jで多くのコメントが付いていたり、そのあたりでいろいろと議論が行われている。

とりあえずこの問題だけに関する議論は置いておいて、もっと一般的な「サイトに脆弱性を発見したときにどうするか?」という問題について考えてみる。

Webアプリ脆弱性通知に関する問題

現在、OSやサーバーなどの特定のアプリケーションについてはJPCERT/CCに報告すれば、脆弱性の問題解決に向けて努力してくれているようである。しかし、クライアントアプリケーションや、かなりユーザーに近いWebアプリケーションについては野放しである。それらは個別に作られていたりするので、一般的な対応が出来ないためであろう。しかし、警鐘●危険なネット銀行を作るな(by ITPro)で指摘されているように、信頼が命であるべき銀行でさえ山のように脆弱性を生産している。今でも安全なWebアプリ開発40箇条の鉄則(by 高木氏)で指摘されているような安易な銀行アプリが存在している。こういう状況をどうしたらよいのか?

朝日新聞一面の件について(by stardust氏) では「Webアプリミシュラン」みたいなWebアプリ評価機関がサポートすべしとある。そこらじゅうにあるWebアプリを集めてきて、信頼できる人・団体が継続的に評価して情報を公開するようなもの。まさにJPCERT/CCがやるべき仕事であろう。

/.Jでは期待通りの議論が盛り上がっているが、たまに興味深い意見もある。

個人情報を漏らしたのは悪い。善意・無知・過失にかかわらず。そこまでは当然の前提として。
セキュリティホールを見つけら、なにをすればいいんでしょうか。

* サービス提供者に通知する。
→無視されておしまい
* 証拠を提示せず、セキュリティホールの存在のみを公開する。
→風評被害にあったと訴えられる
* 証拠 (方法または結果) を公開する。
→それは非常識で違法で犯罪
* 内容証明でサービス提供者に通知した上で、xx日間返事がなかったときに公開。
→裁判で通知方法の妥当性を問われる。『指摘の方法がよほどおかしい』など。恐喝に問われる可能性もあり
* 公平な第三者機関を仲介して……
寡聞なので、適切な第三者機関を知りません。誰か教えて。
* 自分だけ使うのをやめる、見なかったことにする、そもそも探さない、の3択。
ちとナサケナイが、身をわきまえてこのあたりか……

じゃぁ、どうしよう?(/.J)より

現在、専門の第三者機関は上記の通り無い(ごく最近出来たという噂?)。専門でなければマスコミに動いてもらうという手もあるが、ITに疎い記者だったりすると痛いかもしれない。

また、管理者への通知に関しても以下のような罠がある。

  • 匿名での報告 → いたずら扱いで無視される。もしくは、連絡先を要求される。
  • 実名での報告 → 即「この人が不正アクセスしました」と警察に通報される。
  • 証拠が無い → 証拠を持って来いと言われる。
  • やり方を示す → 理解されない。証拠を持って来いと言われる。もしくはad-hocにこっそり直される。本質的には直ってなかったり、ここだけの問題ではない場合が多い。
  • 証拠を示す → 「不正アクセスされました。証拠もあります。」と警察に通報される。

つまり、office氏本人が言うように、『「今回のcgi問題については、たとえそれを知っても一切誰にも言ってはいけなかったのだ」という結論しか持ち得ない状況です。』という状況である。これで良いのか?

欠陥を発見したら?

/.Jでは欠陥を発見しても何もしないのが常識だという主張がたくさん行われていた。根拠として万引き自動販売機の例え話が持ち出されているが、どちらも直接の被害者が消費者(利用者)で無い点が、欠陥報告の例え話として不適当である。ただ、その後の正しい議論で以下の点が指摘された。

  • 通報者がサイトの利用者でなければ、管理者は対応する法的義務は無いらしい
  • 欠陥(脆弱性)で実際に利用者に被害が出るまで、利用者には何の法的権利も無いらしい
  • 欠陥の存在は「欠陥を利用して攻撃される」という可能性があるに過ぎず、可能性では何の権利も主張できないらしい
  • 欠陥を通知しても、単なる修正要望でしかなく、管理者には修正する法的義務は無いらしい
  • 業を煮やして欠陥を公開したりすると、こっそり直されて名誉毀損で訴えられる可能性が高いらしい
  • 実際にサイトの欠陥が攻撃されて、利用者に被害が出れば損害賠償を請求できるが、「どの程度情報が流出したか」などの損害を自分で証明しなければならない
  • その際、管理者側の情報は管理者側に不利になるので、管理者側の協力が得られない場合が多いらしい
  • 普通、攻撃はこっそり行われるので、2chなどで祭りになるまで管理者は攻撃を認識できない可能性が高い
  • ほとんど泣き寝入り・・・

なんだか、「事件が起きないと警察は動けない」と言い訳しているうちに取り返しがつかなくなった桶川ストーカー殺人と同じ構造みたい。つまり、結論は以下のよう。

  • 利用者であれば非公開で管理者に要望はできるが、それ以上はできない。
  • 不安だと思ったら、口をつぐんだまま去るしかない。
  • サイトを利用せざるを得ない場合は、泣き寝入りするしかない。
  • 利用者でなければ欠陥を発見しても何もしてはいけない。

これでは、2chで祭りにならないと動かなかったり、誰にも知られずに情報が漏れ続けている現状は何も変わらない。法律が欠陥を守ってくれると信じている〜な人はこれでもいいのかもしれないが、より良い社会を望むエンジニアとしては許せない。

脆弱性が生み出される現状をどうするべきか?

法的にがんばるのであれば、車のリコールのような制度をコンピューターやネットワークの世界でも整備するのが良さそう。でも、素人が欠陥を大量生産してしまったり、フリーソフトのように一旦広まったら全てを追跡できないような現状の解決は難しいと思う。

やっぱり、第三者機関か政府が欠陥報告用の窓口を作って、情報を集約して、解決に向けて努力するのが無難なのかもしれない。

また、欠陥を作ってしまわないように、セキュリティの意識を開発者だけでなく、素人やデザイナーの人にも周知させる必要がある。特に入門書こそ、セキュリティに関する配慮をしなければいけないと思う。

これからは管理者へ直接欠陥を通知するのは難しい。昔は欠陥通知は普通に行われて、しかもよく機能していた。しかし現在は、通知者は前述のような法的罠にはまってしまう。管理者と知り合いでもなければ、欠陥の通知はしないほうが無難であろう。欠陥技術を公開する場合は、どのサイトに欠陥があるか特定出来ないようにしなければならない。

でもきっと、欠陥報告用窓口が出来てもあまり改善されないと思う。/.Jの議論や、幾ら指摘しても動かなかった管理者、さらに住基などの政府の責任者の発言を見ていると、そこにはとてつもない〜の壁が立ちはだかっているようだ。

Jan.8,2004 (Thu)

続・脆弱性報告

なぜ脆弱性をチェックするのか

脆弱性報告に批判的な人の根拠として、人の家の鍵を勝手にチェックして回る人はいないという主張がある。これは非常に分かりやすい例ではあるが、全ての例をこれでまとめてしまって良いのか?

自分は好き好んでチェックをしに行かないが、偶然発見してしまうということは良くある。例えば過去に以下のようなことを体験した。

  • 重要な情報を入力するのにhttpだったり、サーバー証明書が信頼できないという表示が出る
  • 重要な情報を入力する画面で、アドレスバーやステータスバーが強制的に隠される
  • IDとパスワードを入れてパスワードを間違えた時、「パスワードが違います」と表示される
  • 検索を行うテキストボックスに「<」を含んだキーワード(例えば「a<5」「cout <<」など)を入れると、表示がおかしくなる。
  • 普通に検索を行っていると、SQLのエラーらしきものが出る
  • などなど

これらは日常行う動作であり、特にテストしようと思ってしたわけではない。しかし、上のようなサイトは明らかに欠陥がある。例えば、自分の使っている銀行アプリがこんな症状を出したら、いつ自分の預金が悪人に引き出されてもおかしくない状態といえる。

冒頭に挙げた「隣の家の鍵を勝手にチェック」について考えると、弱い鍵で被害を被るのはチェックされた家だけである。普通、他人の家の鍵が直接多くの人に被害を及ぼすことは無い。よって、この例は脆弱性報告を批判する根拠としては不適当である。また、「勝手に他人の家を探る」ことは日常の行動ではありえず、そういう点からも「脆弱性を報告するような人間は非常識だ」というような非常に悪いイメージを与えてしまう。

脆弱性は探しに行かなくてもそこらじゅうにあふれている。

サービス選択は自己責任か

脆弱性報告の議論で重要なのは、サイトの脆弱性は、そのサイト自身が危険だというより、そのサイトを利用している人全てを危険にさらしているということである。ここを間違えている人とは議論にならない。

サイト脆弱性による利用者被害を例える表現として、被害を拡大、深刻化させる方向に運用されはじめた「不正アクセス禁止法」(by NetSecurity) で指摘されている「公害」は、非常に近い表現だと思う。自分は車や電化製品の欠陥も近いのではないかと思う。このような公害や車などの欠陥は、多くの利用者が日常の生活で被害を被る可能性があり、すばやく情報を広める必要がある。一方、サイト脆弱性では、公開した情報から被害がさらに広まる可能性があり、対策を行ってからでないと情報を公開できない。このあたりの違いが、現実世界の方法がそのまま適用できない原因になっているのだろうと思う。

自分にとって、銀行口座を別の銀行に移すのは非常に手間がかかるので、自分の都合でない限り出来れば変えたくない。隣の空き地に産廃が運び込まれるようになったので住み慣れた家から別の場所に引っ越せばよい、買った新車に欠陥が発見されたから買い換えればよい、住基ネットに欠陥があるから国籍を変えればよいなど、言うのは簡単だけれども他人の都合でそう簡単に乗り換えられるものだろうか。初めから欠陥があると分かっていれば誰もそんなものを選択はしない。後から状況が変わったときに、それに対応できるかなどその人本人でなければ決められない。なので、そんなサービスは使わなければ良いという意見は、あまりにも想像力の欠けたものだと思う。

それよりも、状況の変化に対応しなければいけないのは管理者の方であろう。サービス提供側の約款には大抵「相当の安全対策を講じる」みたいなことが書いてある。常識的に考えて管理者には脆弱性が発見されたら修正の義務があるはず。事前に脆弱性が発見・通知されておきながら放置することは約束を守ってないことになる。これでは、「これによって生じた損害については当行は一切責任を負わないものとします。」と言われても、利用者も責任を負えない。

よって、利用者だけに責任を押し付けるのもどうかと思う。

Pnuts, JavaCC

Pnuts (@java.net)
最近はWebのスクリプト言語として使えるようにも開発が続いているらしい。

JavaCC (@java.net)
現在は java.net の中でオープンソースプロジェクトに移行していた。JavaCC Tipsも多少修正。

Jan.18,2004 (Sun)

気が付いたらカラスが鳴いていたりとか。

JavaのGUI構築

JavaでGUI構築は手間がかかる。JBuilderやNetBeansを使えばGUIでデザインできるが、コードが縛られているようで気持ち悪い。

でも、Swingは量が多いと手間がかかるけども、コード自体は素直なのでそれなりに早く書けるし保守性も非常に良い。

UIHierarchyはJavaのGUIライブラリを見直して、GUI構築・レイアウトに特化したコーディングが出来るようにしたもの。XMLでGUIレイアウトだけ外にくくり出せるので、この方法ならSwingのいいところをさらに引き出せそう。

ソフトウエアの自由

各地で議論があるが、本気で裁判にでもならないと明確な判断が出来ないみたい。あと、特許と著作権とソフトウエアライセンスがごっちゃになっている人も多いような。

とりあえず、自分がどうしたいのかということから考えてみる。
まだまだ自分なりの結論はでそうにない。

それにしても、旧ZDNetのリンクがことごとく切れて痛い。

Jan.27,2004 (Tue)

ウイルスまみれ

昨日の夜くらいから大量のウイルスを受信。

Bluetooth 携帯

C413S(携帯)が壊れたので修理に出す。「もう寿命・・・」みたいなことを言われた。しかし、Bluetooth対応の携帯なんて他に無い。外付けのBluetoothオプションは冗談のような大きさと値段なので買う気が起きない。

日本以外ではBluetoothは盛り上がっているみたいなのだけど、どこもC413Sの後継など作ってくれないのだろうか。

Dual Monitor で VNC

現在、以下のような構成。

x2vnc 1.5.1 (TL8WS on i8k) → Ultr@VNC 1.0.0 R11 (WindowsXP on X31)

ここで、WindowsXPをデュアルモニタにしてみると、2つ目のモニタにマウスが移動しない。
原因は、VNCのサーバーがデュアルモニタに対応していないためらしい。しばらくGoogleすると、RealVNC 4.0 の beta4 からデュアルモニタがサポートされたという記述を見つける。
さっそくRealVNC4b4を入れてみると、うまくマウスカーソールが移動するようになった。

ディスプレイが3面 (+CLIE 1面) になって、とりあえず満足する。

Jan.29,2004 (Thu)

雑用を沢山こなす。

JavaでMixin

Traits(by Matzにっき)付近を読んでJavaのMixinについて考えてみる。

Javaは型チェックの恩恵が得られる代わりに、柔軟なプログラムがやりにくい。また、型重視な割りにキャストと多用してしまうのでその辺りも気持ち悪い。ここで言う柔軟とは説明は難しいけども、Rubyで簡単に出来てJavaで困ってしまうようなところとか。Mixinもそのひとつ。

Mixinは継承の1つのタイプで、単に実装の共有を目的にした継承。詳しくは CMagazine2004年1月号「多重継承は本当にいらないか?」(by επιστημη氏)などを参照。

JavaでMixinを実現するには多重継承をクリアしなければならないが、そのままの言語仕様では委譲によって多重継承を迂回しなければならない。

委譲による多重継承

よく使われる例としてはObserverパターンがあるが、あまり適用できる場面は少ないと思われるので、Comparableな実装を例にしてみる。

java.lang.Comparable は順序決定のためのインタフェースで、このインタフェースを実装すれば java.util.Collections#sort などでソートできるようになる。しかし、このインタフェースの実装はちょっとめんどくさくて、キャストやどっちが大小かといった細かい仕様を覚えるのが大変。

例えば以下のようなインタフェースを定義してみる。


package test;
 
interface ComparableInteger extends Comparable {
 
    //とりあえず整数を返すようにする
    abstract int getValue();
 
}

次に、次のような実装を入れることで非常に使いやすくなる。


package test;
import java.util.Collections;
import java.util.ArrayList;
 
//面倒な順序付けを行うクラス
//これはこれで結構汎用的
abstract class ComparableIntegerImpl implements ComparableInteger {
 
    //とりあえず、整数を返してくれれば順序付けはこのクラスが行う
    //int getValue();
 
    public String toString() {
        return "Comparable:"+getValue();
    }
 
    public int compareTo(Object o) {
        if (o instanceof ComparableInteger) {//この辺が面倒!!
            ComparableInteger t = (ComparableInteger)o;
            return this.getValue() - t.getValue();
        }
        throw new IllegalArgumentException();
    }
}

例えばこんな感じで、値を返すだけで使えるようになる。


//上の使用例:単にgetValueを実装するだけで並び替え可能になる
class ComparableIntegerSample extends ComparableIntegerImpl {
  
    private int number;
    ComparableIntegerSample(int i) {
        number = i;
    }
 
    public int getValue() {
        return number;
    }
 
    //使用例 
    public static void main(String[] args) {
        //とりあえず、適当に生成してソートする
        ArrayList list = new ArrayList();
        list.add(new ComparableIntegerSample(4));
        list.add(new ComparableIntegerSample(1));
        list.add(new ComparableIntegerSample(10));
        list.add(new ComparableIntegerSample(3));
        list.add(new ComparableIntegerSample(7));
        Collections.sort(list);
        for(int i=0;i<list.size();i++) {
            System.out.println(list.get(i));
        }
    }
}

これで、並び替えたいクラスにこのComparableIntegerImplクラスを継承させることで、簡単にComparableを実装することが出来る。

しかし、既に何か継承されているクラスにこのComparableIntegerImplクラスを継承させることは出来ない。その場合は、次に示すような委譲を使わなければならない。


package test;
import java.util.Collections;
import java.util.ArrayList;
import javax.swing.JLabel;
 
//JLabel と ComparableInteger の両方の機能を持たせたい
//2つは同時に継承できないので、片方のみ extends してもう片方は implements にする。
class ANumberLabel extends JLabel implements ComparableInteger {
 
    private ComparableInteger delegate;
    private int value;
 
    ANumberLabel(int i) {
        super(Integer.toString(i));
        value = i;
        //委譲先を生成!
        delegate = new ComparableIntegerImpl() {
                public int getValue() {
                    return value;
                }
            };
    }
 
    //委譲!
    public int getValue() {
        return delegate.getValue();
    }
    //委譲!
    public int compareTo(Object o) {
        return delegate.compareTo(o);
    }
    //委譲!
    public String toString() {
        return delegate.toString();
    }
}
 
class DelegateSample {
    public static void main(String [] args) {
        //とりあえず、適当に生成してソートする
        ArrayList list = new ArrayList();
        list.add(new ANumberLabel(4));
        list.add(new ANumberLabel(1));
        list.add(new ANumberLabel(10));
        list.add(new ANumberLabel(3));
        list.add(new ANumberLabel(7));
        Collections.sort(list);
        for(int i=0;i<list.size();i++) {
            System.out.println(list.get(i));
        }
    }
}

今回のケースは簡単すぎるので、委譲するよりもComparableを実装してしまった方が早い。こんな例は実際には無いかも知れないので、あくまで一つの例として。ただ、委譲は強力なテクニックであるので、委譲自体が悪いわけではない。動的に振る舞いを変えられたり、実装の中身に深く立ち入らなくて良いなどの多くの利点がある。

アスペクト指向プログラミングによるMixin

JavaでMixinを実現するためには、MixJuiceAspectJなどのアスペクト指向プログラミング(AOP:Aspect Oriented Programing)が広く使われているみたい。MixJuiceは日本人が開発しているので、ドキュメントなどが分かりやすいかもしれない。AspectJは最も広く使われている(と思う)アスペクト指向言語で、詳しくはAspectJ Tips and Pitfalls(by てぃる氏) などを参照。

上の例をAspectJで書き換えてみる。方針的には、委譲コードをweaveする感じ。


package test;
import java.util.Collections;
import java.util.ArrayList;
import javax.swing.JLabel;
 
//JLabel と ComparableInteger の両方の機能を持たせたい
//片方は普通に継承して、もう片方はAOPで後付け
class BNumberLabel extends JLabel {
 
    private int value;
 
    BNumberLabel(int i) {
        super(Integer.toString(i));
        value = i;
    }
 
    public int getValue() {
        return value;
    }
}
 
//もうひとつ 
class BStringLabel extends JLabel {
    BStringLabel(String s) {
        super(s);
    }
    public int getValue() {
        return getText().length();
    }
}
 
//後付け!
aspect ComparableIntegerMixin {
    private interface MixinTarget {
        public int getValue();
    }
    declare parents: BNumberLabel||BStringLabel implements MixinTarget,ComparableInteger;
 
    public int MixinTarget.compareTo(Object o) {
        if (o instanceof ComparableInteger) {
            ComparableInteger t = (ComparableInteger)o;
            return this.getValue() - t.getValue();
        }
        throw new IllegalArgumentException();
    }
}
 
class MixinSample {
    public static void main(String [] args) {
        //とりあえず、適当に生成してソートする
        ArrayList list = new ArrayList();
        list.add(new BNumberLabel(4));
        list.add(new BStringLabel("sample"));
        list.add(new BNumberLabel(10));
        list.add(new BNumberLabel(3));
        list.add(new BNumberLabel(7));
        Collections.sort(list);
        for(int i=0;i<list.size();i++) {
            System.out.println(list.get(i));
        }
    }
}

なんと、BNumberLabel,BStringLabelには並び替えのためのインタフェースを実装していない。これはまさにRuby風。aspect部分以外は非常にシンプルであるし、並び替え機能が後付けの場合、既存のプログラムの書き換えがほとんど発生しない。

しかし、共通ライブラリみたいな感じには汎用化できるかは分からない。また、BNumberLabel,BStringLabelにはMixinの情報が全然ないので、プログラムが分かりにくくなるかもしれない。

AspectJ を使ったデザインパターンの改善と支援(by asato氏)のAspectJ Tips: MixinAspectJ Tips: Traitsには、もうすこしスマートな例が示してある。Mixinを目的にしたMixJuiceの方も良い解決なのだと思う。

今のところ、Mixinしたい機会はそんなに少なくないけども、AOPまで持ってきてMixinしようと思ったことはない。

Jan.31,2004 (Sat)

コンピューターのパフォーマンスに引きずられた日。結果が出たのが遅かった。

JNIとNIOとパフォーマンス

最近のJava実行系はかなりパフォーマンスの最適化が進んでいるので、Cなどの静的コンパイル言語で作ってもあんまり大差が無い。大抵オーダーは同じか、数値計算専用ライブラリが出てこなければ差は大きくても数倍以内。

最近というか、今更ながら java.nio の非ヒープバッファを使ったJNI(Java Native Interface:Javaとネイティブライブラリとの接続規格)への高速大量データ渡しについて知った。JNIのボトルネックはJavaとネイティブライブラリの間のデータ転送だったので、これが改善されるのならパフォーマンスをもっと稼げるのではと思って実験してみた。

X31i8k
CPUPentium M 1.6GHzMobile Pentium III 1.13GHz
Main MemoryPC-2700 DDR 1GByteSDRAM PC133 512MByte
OS Windows XP ProTurbolinux 8 WS
JVM Pure(msec) JNI(msec) JNI+NIO(msec)
Win32 Sun 1.4.2_03 1846 2806 2808
Win32 Sun 1.4.1_06 1938 1536 1518
Win32 Sun 1.3.1_10 2373 2816 ---
Win32 IBM 1.3.1 1474 2804 ---
Linux Sun 1.4.2_03 2593 1751 1799
Linux Sun 1.4.1_02 2626 1739 1808
Linux IBM 1.4.0 2091 1704 1817

WindowsのJNIに関しては、SunVM1.4.1 以外は全部性能の低下が起きている。Javaとネイティブライブラリ間の値コピーが原因らしい。NIOを使ってもぜんぜん性能の向上がない原因が分からない。自分のソースが悪いのかも知れない。

Linuxに関してはJNIはかなり良い結果を示しているが、やっぱりNIOの効果がないらしい。ここでは示していないが、行列の規模を大きくすると性能の開きは大きくなる。しかし、数十倍のパフォーマンス向上は見込めないらしい。特にIBMのVMではネイティブコンパイラとVMの差はほとんどない。

NIOで性能改善が出来てないので本当にこの結果を信用していいのか分からない。NIOについてもう少し調べてみる必要がある。

ということでここで確実に言えることは、下手なJNIは性能を改善しないし、かえって悪化させるというもの。

ソースなど

MatrixNIOTest.java


import java.nio.*;
 
public class MatrixNIOTest {
 
    public static void main(String[] args) {
        System.loadLibrary("minio");
        int num = 500; //500x500の行列
 
        MatrixNIOTest m = new MatrixNIOTest(num);
        double[] array = null;
        double[] ret = null;
 
        array = m.getArray(num);
        ret = m.getArray(num);
    
        long tp = 0,tn = 0,tj = 0;
        int it = 5;   //5回の平均
        for (int i=0;i<it;i++) {
            tp += m.testJava(num,array,ret);
            tn += m.testNio(num,array,ret);
            tj += m.testJni(num,array,ret);
        }
 
        System.out.println("pure : "+(tp/it));
        System.out.println("nio  : "+(tn/it));
        System.out.println("jni  : "+(tj/it));
    }
 
    private int num;
    private DoubleBuffer adoubleBuffer,rdoubleBuffer;
 
    MatrixNIOTest(int num) {
        this.num = num;
        //ダイレクトバッファ生成
        ByteBuffer abuffer = ByteBuffer.allocateDirect(num*num*8);
        abuffer.order(ByteOrder.LITTLE_ENDIAN);
        adoubleBuffer = abuffer.asDoubleBuffer();
        ByteBuffer rbuffer = ByteBuffer.allocateDirect(num*num*8);
        rbuffer.order(ByteOrder.LITTLE_ENDIAN);
        rdoubleBuffer = rbuffer.asDoubleBuffer();
    }
 
    //PureJavaによる掛け算 
    long testJava(int num,double[] array,double[] ret) {
        long st = System.currentTimeMillis();
        multPure(num,array,ret);
        long t1 = System.currentTimeMillis()-st;
        System.out.println("Pure:"+t1);
        return t1;
    }
 
    //JNIによる掛け算 
    long testJni(int num,double[] array,double[] ret) {
        long st = System.currentTimeMillis();
        multJni(num,array,ret);
        long t1 = System.currentTimeMillis()-st;
        System.out.println("JNI:"+t1);
        return t1;
    }
 
    //JNI+NIOによる掛け算 
    long testNio(int num,double[] array,double[] ret) {
        long st = System.currentTimeMillis();
        adoubleBuffer.clear();
        adoubleBuffer.put(array);//この部分の実行時間は誤差の範囲内
        multNio(num,adoubleBuffer,rdoubleBuffer);
        adoubleBuffer.rewind();
        adoubleBuffer.get(ret);  //この部分の実行時間は誤差の範囲内
        long t2 = System.currentTimeMillis()-st;
        System.out.println("NIO:"+t2);
        return t2;
    }
 
    /** 適当な行列を生成 */
    double[] getArray(int num) {
        double[] a = new double[num*num];
        for(int i=0;i<a.length;i++) {
            a[i] = Math.random();
        }
        return a;
    }
 
    /** Javaで掛け算を計算 */
    void multPure(int num,double[] array,double[] ret) {
        for(int i=0;i<num;i++) {
            for(int j=0;j<num;j++) {
                double b = 0;
                for(int k=0;k<num;k++) {
                    b += array[i+k*num]*array[k+j*num];
                }
                ret[i+j*num] = b;
            }
        }
    }
 
    /** JNI+NIOで掛け算を計算 */
    native void multNio(int num,DoubleBuffer array,DoubleBuffer ret);
 
    /** JNIで掛け算を計算 */
    native void multJni(int num,double[] array,double[] ret);
}

minio.cpp


#include "MatrixNIOTest.h"
 
/*
  NIOのDirectBufferでデータを渡す。
*/
JNIEXPORT void JNICALL Java_MatrixNIOTest_multNio
(JNIEnv *env, jobject caller, jint num, jobject arrayObj, jobject retObj) 
{
  int i,j,k;
  double b;
  jdouble *array = (jdouble*)env->GetDirectBufferAddress(arrayObj);
  jdouble *ret = (jdouble*)env->GetDirectBufferAddress(retObj);
  
  for(i=0;i<num;i++) {
    for(j=0;j<num;j++) {
      b = 0;
      for(k=0;k<num;k++) {
        b += array[i+k*num]*array[k+j*num];
      }
      ret[i+j*num] = b;
    }
  }
};
 
/*
  普通の配列でデータを渡す。
*/
JNIEXPORT void JNICALL Java_MatrixNIOTest_multJni
(JNIEnv *env, jobject caller, jint num, jdoubleArray arrayObj, jdoubleArray retObj) 
{
  int i,j,k;
  double b;
  jdouble *array = env->GetDoubleArrayElements(arrayObj, NULL );
  jdouble *ret = env->GetDoubleArrayElements(retObj,NULL );
  
  for(i=0;i<num;i++) {
    for(j=0;j<num;j++) {
      b = 0;
      for(k=0;k<num;k++) {
        b += array[i+k*num]*array[k+j*num];
      }
      ret[i+j*num] = b;
    }
  }
  
  env->ReleaseDoubleArrayElements(arrayObj, array, JNI_ABORT );
  env->ReleaseDoubleArrayElements(retObj, ret, 0 );
}
Windowsでのコンパイル(BCCを使用)と実行
$ javac MatrixNIOTest.java
$ javah MatrixNIOTest
$ bcc32 -O2 -OS -I$JAVA_HOME/include -I$JAVA_HOME/include/win32 -tWD minio.cpp
$ java MatrixNIOTest
Linuxでのコンパイルと実行
$ javac MatrixNIOTest.java
$ javah MatrixNIOTest
$ gcc -O3 -fPIC -g -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -c minio.cpp
$ gcc -shared -o libminio.so minio.o
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`
$ java MatrixNIOTest


[最新にもどる]
桜井雅史: E-mail : m.sakurai@cmt.phys.kyushu-u.ac.jp
Web page : http://www.cmt.phys.kyushu-u.ac.jp/~M.Sakurai/