torutkのブログ

ソフトウェア・エンジニアのブログ

JavaOne 2013 SF(09-24)

JavaOneの3日目(セッション2日目)(暫定版)です。
今日は、朝8:30から夜21:00過ぎまで長い1日でした。

本日聴講したセッション一覧は次です。

  • The JavaFX API's Synergy with JavaFX 3D (8:30-9:30)
  • Integrating JavaFX with Native Technologies (10:00-11:00)
  • The Modular Java Platform and Project Jigsaw (11:30-12:30)
  • Practice and Tools for Building Better APIs (13:00-14:00)
  • Wholly Graal: Accelerating GPU Offload for Java (15:00-16:00)
  • Techniques for Distributing Java Desktop Applications (16:30-17:15)
  • Project Sumatra Update (17:30-18:15)
  • JVM Bytecode Engineering 101 (19:30-21:30)

The JavaFX API's Synergy with JavaFX 3D【CON3400】

講師はAlexander Kuznetcov氏, Joe Andresen氏、オラクル社のエンジニアです。

JavaFX 3Dでは、ポリゴングラフィックス(平面三角形を組み合わせて物体の表面を表現する)が可能になりますが、その際テクスチャマップ(ポリゴンの表面に画像を貼ることで現実感のある物体を表現する)で使用する画像をJavaFXの2D用APIで作りますよという内容が前半、次は3D表示でマウス操作したときに3Dの物体のどこを示すかを取得する方法とそれを使った3Dお絵描き(球体の表面にペイントのようにマウスでドローする)のデモです。

講師は2人のはずですが、ずっと1人だけで話しており、途中デモを飛ばして30分ほどでQ&Aに入ってどうしたのかと思いましたが、40分過ぎくらいにデモ担当の人が到着して最後にデモを見ることができました。なお、彼は講師本人か代役か不明です。

JavaFX 8のCanvasは描画可能なカスタムテクスチャとして利用できます。

canvas = new Canvas(300, 300);
g = canvas.getGraphicsContext2D();
g.setFill(Color.gray(0.6));
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());

次にノードをイメージに変換するためにSnapshotParametersを使います。

SnapshotParameters params = new SnapshotParameters();
params.setCamera(new PerspectiveCamera());
params.setFill(Color.GREEN);
Image img = canvas.snapshot(params, null);

JavaFX 3Dのポリゴン表示ノードであるMeshViewのサーフェイス(表面)に先に作成したイメージを貼ると、3Dグラフィックスのテクスチャマッピングができます。JavaFX 3Dでは、サーフェイスをどう描画するか(サーフェイスの材質、光の反射モデル)をMaterialとして指定します。本セッションでは例題としてPhongMaterialを使用し、setDiffuseMapでイメージを貼っていました。

material = new PhongMaterial();
material.setDiffuseMap(img);
meshView.setMaterial(material);

次は、3次元表示をしているシーン上でマウスクリックをしたときに、3次元的にどこをポイントしているかを判別してアクションをする例を紹介しました。


この画面のように球体を表示し、マウスで球体上をドラッグしたらその位置にペイントをするというデモです。


こんな感じになります。

3Dピッカーについては、公開されたスライドにはコード断片の紹介がなぜか割愛されていたので、当日とったメモを記載します(Javadocと照合を取っていません)。

Point2D cxy = t.getPickResult().getIntersectedTexCoord();
x = cxy.getX() * canvas.getWidth();
y = cxy.getY() * canvas.getHeight();
  :
Point3D txPoint = ... // ローカル座標をワールド座標に変換する

また、サーフェイスの表示について、ここではバンプマッピングをしていて、HeightMapがどうとうか言っていました。PixelRender、Bash Pixels?

ペインティングでは、ブラシが用意されています。
性能面では、マウスイベント毎にスナップショットをとらないこと、できればAnimationTimerを使う、テクスチャサイズに注意、WritableImagesを再利用するとよい、とのことです。

Q&Aメモ
シェーダーはない、GPUは使わない、Minecraftの大きな空間(世界の果て?)のテクスチャの話などが出ていました(細部は聞き取れず)。

Integrating JavaFX with Native Technologies【CON1905】

講師はFelipe Heidric,氏 Steve Northover氏、オラクル社のエンジニアです。

このセッションの本題は、JavaFXOpenGLを使う方法を探求するというもので、現時点ではまだ確定したライブラリ等があるわけではありませんでした。

JavaFXアーキテクチャ図に必ず登場するGlassとPrismの話から始まりました。
Prismの実装内部を使うといろいろネイティブな操作ができますが、公開APIではないので要注意と断りを入れて、まずはMacOS Xの印刷ダイアログをJavaFXで呼び出す方法とデモがありました。

JavaFXライブラリおよびGlassではUIスレッド、Prismはレンダースレッドで処理が実行されます。
OpenGLはステートマシンなので、レンダースレッドでステートをクエリーします。
JavaからOpenGLを利用するには、JOGL、JLWGL、Eclipse Platform Generatorがあるそうです。
JavaFXからOpenGLを使うには、

  1. バイトバッファに書いてアップロードする
  2. Prismのドローに合わせてドローする

方法が考えられます。

NGNode(?)クラスのrenderContentメソッドをオーバーライドし、Prismが内部で使用しているOpenGLの呼び出しを利用してしまうという感じです。ここはソースコードを次々見せて進んだので理解が追い付きませんでした。断片のメモを記します。

レンダースレッドからUIスレッドの状態にアクセスしてはなりません。
アプリケーションはUIスレッドで動作します。
getPrismTexture()、glflush()などが呼び出せます。

The Modular Java Platform and Project Jigsaw【CON9732】

講師はMark Reinhold氏、+助手

新しい話はなかったような気がします。デモで実行していたコマンドもjigsawの開発ビルドで用意されていたものだったはず。

jlinkというコマンドでいろいろデモをしていました。去年のjigsawにはなかったコマンドです。

小型デバイス用途に、Java SEの実行環境を分割し、

  1. Compact1(logging, script, tls
  2. Compact2(jdbc, rmi, jta, jaxp)
  3. Compact3(compiler, prefs, rough set, naming, auth)
  4. JRE

とサブセットを提供します。

拡張子.jmodのファイルをjlinkで処理するという説明、デモ

Practice and Tools for Building Better APIs【CON4374】

講師はオランダの人でPeter Hendriks氏

APIのよい設計の仕方を解説したセッションです。
APIの呼び出し側(comsumer)と提供者(implementer)という観点で検討していきます。
APIはモジュール設計を成功させる鍵であり、大規模ソフトウェアにおいて再利用、独立して変更、関心の分離に不可欠なものです。

コードの大半がAPIの呼び出しに費やしているので、APIはコードを簡潔に保つかどうかに関わります。例えば、SAXのリードとJASONのリードでは前者はかなりコード量が多くなります。

よいAPI

  1. 価値がある(価値=利益ーコスト)
  2. 使いやすい(学びやすい、生産的、エラーしにくい)
  3. 安定している(既存コードを壊さずに機能を高める)

API設計はヒューマンインタフェース設計、それはプログラマーAPIを解釈して使うものだからです。

APIの使いやすさ

  • 学習しやすい
  • 効率さ
  • 記憶しやすさ
  • エラー
  • 満足さ

API設計の観点

  • UML(名前、完全性、グルーピング)
  • APIの使いやすさ
  • APIの実装

API設計は、使用例から始めよう。
APIドキュメントで最も重要な記述は使用例です。
チートシートを作るのとよい。

小さいAPIは使いやすく維持しやすい、小さなクラス、小さなメソッド、少ない引数など。
複雑なAPIを削除/変更するよりも小さなAPIを追加する方が容易→安定性が増す。

APIを利用する側のコードが最小となるように設計します。
APIを利用する側のコードを増やす要因

  • クラスの継承、インタフェースの実装をさせる
  • チェック例外
  • キャストの必要性
  • ジェネリック型の必要性
  • 引数に値をハードコーディング
  • if, for, while, tryの必要性
  • synchronzie/同期の必要性

log4jとslf4jのAPIを使う例

// log4jのAPI使用側
if (logger.isDebugEnabled()) {
    logger.debug("The new entry is " + entry + ".");
}
// SFL4JのAPI使用側
logger.debug("The new entry is {}.", entry);

Listのイテレート例(拡張for文よりメソッド化、さらにラムダ式の記述がシンプル)。

メソッドは、呼ぶ順序に制約がないのがよく、コンストラクタは構築に徹し、思わぬ副作用がないのがよいです。

nullの使用は最低限に抑え、誤った入力にはフェイルファースト、フェイルハードがよいです。

Objects.requireNonNull(x)

FindBugsとJSR305の@Nonnull、@CheckForNull、@Nullableなど

依存関係を可視化するDSM(Dependency Structure Matrix)

最後に、API設計のチートシートを示します。

Wholly Graal: Accelerating GPU Offload for Java【CON6419】

講師はChris Thalinger氏(Oracle)、Christian Wimmer氏(Oracle)、Vasanth Venkatachalam氏(AMD)です。

GraalとはOpenJDKプロジェクトの1つ、HotSpot VMのためのJITコンパイラです。
Javaで実装され、HotSpot VMJITコンパイラを置き換える、または統合して一部のメソッドのJITコンパイルを担当するというものです。特徴は、GPGPUで処理を実行するコードを生成する点です(GPUオフロード)。

Graalは、Javaバイトコードだけではなく、Truffie API(Dynamic Language Frontend)と呼ばれる仕組みを用意してJavaScriptRubyPython、Rなどのスクリプトコードも対象とすることが可能です。

GraalはOpenJDKプロジェクトで、次のリポジトリからソースを取得してビルド・使用することが出来ます。
http://openjdk.java.net/projects/graal/

$ hg clone http://hg.openjdk.java.net/graal/graal
$ cd graal
$ ./mx build
$ ./mx vm
$ ./mx ideinit
  • 後日、プロジェクトサイトを見るとWindowsでもビルドできるようです。

GraalはProject Sumatraで開発しているプロトタイプで使用されています。Project Sumatraはヘテロジニアスな処理ユニット(GPU、APU)上でアプリケーションを動作させようというものです。説明のスライドでは、GraalはCPU実行コード、PTX(nVIDIAのCUDAアセンブリ言語コード)、HSAIL(特定のCPU/GPUに依存しない中間形式)をJITコンパイル結果として生成します。

現在のGraalの性能は、次の結果です。

  • CPU: Core i7 3770 3.4GHz (4core 8HT)
  • メモリ: 16GB
JITコンパイラ SPECjvm2008 Scala-Dacapo
HotSpot Client 76 61
HotSpot Server 114 106
Graal 100 100

ヘテロジニアスなシステムのアーキテクチャとして共通の中間形式言語(HSAIL)を規定しています。開発者は普通にJavaでプログラムを書くだけです。
GPUとCPUでメモリを共有(Shared Virtual Memory)することでGPUオフロードのデメリットであるデータ転送をなくそうという考えがあります。

for (int i = 0; i < in.length; i++) {
    out[i] = in[i] * in[i];
}

というコードをJavaで書いた場合、Graal JIT搭載JavaVMは次のようなHSAILをまず生成し(ASCIIテキスト形式)、

mul_s32 $s3, $s0, $s1

"finalizer"と呼ぶ機構で実行時にGPU依存コードに変換します。

Sumatraプロトタイプでは、Java 8のコード(Streams API)をHSAILに変換します。実際にマンデルブローのプログラムで性能評価をしたところ、JavaのスレッドでCPU上の並列処理をしたのに比べGPUを使った方が10倍速い結果を得ました。

例)

IntStream.forEach( i -> {
  out[i] = in[i] * in[i];
});

このラムダ式を使ったJavaソースコードはjavacで次のバイトコードコンパイルされます。

private static void lambda$67(int[] out, int[] in, int i) {
  out[i] = in[i] * in[i];
}

これがGraalでHSAILの次のコードに変換されます。

kernel &run (
  kernarg_u64 %_arg0,
  kernarg_u64 %_arg1
) {
  ld_kernarg_u64 $d6, [%_arg0];   <- パラメータ渡し
  ld_kernarg_u64 $d2, [%_arg1];   <- パラメータ渡し
  workitemabsid_u32 $s1, 0;       <- 現在のワークアイテムIDをロード
  
  cvt_s64_s32 $d0, $s1;
  mul_s64 $d0, $d0, 4;
  add_u64 $d2, $d2, $d0;
  ld_global_s32 $s0, [$d2 + 24];
  mul_s32 $s3, $s0, $s0;
  cvt_s64_s32 $d1, $s1;
  mul_s64 $d1, $d1, 4;
  add_u64 $d6, $d6, $d1;
  st_global_s32 $s3, [$d6 + 24];
  ret
}

他にJavaコードでGPUオフロード対象となるのは、現時点では

  • Mathクラスのメソッドの一部(Math.sqrt(in[i]) * in[i])
  • 配列、文字列操作
  • instanceof 演算

このセッションは、参加者が途中でバシバシ質問する他、終盤では参加者同士が議論を交わしていました。議論のアクティブな人を見ると、SPEAKERの札がついているひとが多かったです。

Techniques for Distributing Java Desktop Applications【BOF4085】

講師はJiri Rechtacek氏(Oracle)。

"Distributing"を「分散」と思って聴いてみたら、「配布」、すなわち"Deployment"のことでした。

最初はjavaアプリケーションのパッケージング方式の解説から入っていました。Web StartとNative Packagingを比較していました。

Web Start

(+) 十分確立されている
(+) 常にアップデート可
(-) 前提条件必要(コンフィグレーション、サポートバージョンJRE

Native Packaging

(+) 事前にランタイム不要
(+) AppStore的な配布が可能
(-) サイズが大きい
(-) プラットフォーム依存あり

NetBeansでアプリケーションを作成するデモをしてました。

Project Sumatra Update【BOF6223】

講師はJohn Rose氏(Oracle)、Gary Frost氏(AMD)、Thomas Deneau氏(AMD

データ並列コンピューティングは、分割統治でデータを分解し、同じ命令でデータを変えて並列処理を行います。
一方、JavaVMが用意している並列の仕組みはスレッドですが、スレッドがお互いにリソースを取り合うので、処理ではなくデータを分離する必要があります。
そこで、バイトコードをPTX(nVIDIAGPUアセンブリ言語)に変換して実行する方法を目指します。
例としてJavaの次のコードを

public static int testAddIConst(int a) {
  return a + 32;
}

PTXのコードに変換します。

.version 1.4
.target sm_10
.entry testAddIConst (
        .param .s32 param0,
        .param .s32 retReg
) {
        .reg .s32 %r0;
        .reg .s32 %r1;
L0: 
        ld.param.s32 %r0, [param0 + 0];
        add.s32 %r0, 32;                 <= ここで Javaコードの a + 32 を計算
        ld.param.u32 %r1, [regReg + 0];
        st.global.s32 [%r1 + 0], %r0;
        ret;
}

PTXは、nVIDIAGPUで実行するコードですが、CPUとGPUを標準化するヘテロジニアスなアーキテクチャとしてHSAおよびその命令セットHSAILを定義しています。CPUとGPU間で仮想メモリの共有も目指します。

JDK 8のParallel Streamsの幾つかをGPUオフロード可能にするGraalコンパイラを使用します。プロトタイプでは、IntStream、Object配列、vectorArrayListなどのforEachを見つけるとHSAILを生成するようにしています。
GPUオフロードした命令(カーネル)中では、ヒープアロケート不可、例外ハンドル・try/catch不可などの制約があります。

最後に今後の課題を列挙していました。

HSAILシミュレータを用意するとか言っていたかもしれません。

JVM Bytecode Engineering 101【TUT7610】

講師はKees Jan Koster氏です。

2013-10-18現在講師によるセッションに関する投稿が次のURLにあります。(文中にスライドへのリンクもあり)

スライドは次のURLにあります。

夜ご飯(&ビール)で遅れて参加しました。
バイトコードインストゥルメンテーションのデモが終わろうとしたときに入ったようです。一番面白そうな話題だったのですが聞きそびれてしまいました。
その後は、printlnのコール、コンストラクタ、キャスト、例外などのコードをjavapコマンドを使ってJavaVMのバイトコードで見て説明するということが続いていました。こちらはJavaバイトコードの入門的なお話です。
インデックス0はthisポインタです、i2bはintをbyteにキャストする命令、ラインナンバーテーブルはJavaソースコードの行番号を格納しています、などの話題が続きます。

Eclipseバイトコードアウトラインプラグインというのがあるそうです。

後半は、Q&A形式で進み(「何か聞きたいことは?」)、javasistとbcelの使い分けの質問などが出ていました。

本日のお昼


今日のランチです。昨日はサンドイッチがないのでサラダだけのランチでしたので、12:30-13:00の休み時間に、すいているというニッコーホテル(ヒルトンホテルの隣)へ移動し、そこで食べてヒルトンホテルに戻って次のセッション会場に移動しました。

本日の夕食

きしださん、大山さんとPowell St.駅近くのMikeller Barでビールとサンドイッチを食べました。

OTN

Sunと統合する前、Oracleは開発者向けの情報をOTN(Oracle Technology Network)というブランドで集めて公開してました。今回、JavaもOTNの一部として整備され、トップページが次のようになっています。

Oracle Technology Network for Java Developers | Oracle Technology Network | Oracle