2014/05/18

JJUG CCC 2014 Spring

このエントリーをはてなブックマークに追加

あっという間に月日は過ぎて、半年ごとの blog になりつつあります。

3 月に Java SE 8 がリリースされて、それから怒濤のような月日を送っております。GW も 4/26 に富山、5/6 に岡山で Project Lambda のハンズオンをやってきたりしていました。

そして、何の因果か、JJUG CCC でいつもは会長が喋っている枠で喋らなくてはいけないことになってしまいました。

今回の CCC はなぜか濃い内容のセッションばかり。1 つぐらいはジェネラルな話を入れるべきではということで、Java SE 8 全体の話をすることになりました。

とはいうものの、私の次に Stuart Marks さんによる Project Lambda の話があるので、そこは控えめにという感じです。

 

 

こういう全体を話すプレゼンって、機能だけ並べてしまうと、ぶち切れのプレゼンになってしまって、ストリーも何もあったものじゃないプレゼンになりがちです。しかも、個々の機能が並んでいるだけなので、どれが重要なのかとか、どういう役に立つのかとかが伝えきれなかったりします。

今までも、機能ごとの開発者をフィーチャーしたり、機能を導入した背景を喋ったりとかいろいろ工夫してきたのですが、やっぱり難しい。

ということで、この時は新しい機能がどういう時に使えるかということに着目して話を進めました。

トピックとしては、以下の 6 項目。

  • つい制御構造が複雑に... -> Project Lambda
  • パフォーマンスが... -> Flgith Recorder/Mission Control, パラレルストリーム, ヒープ使用量削減, パーマネント領域廃止
  • Date/Calendar がつくづくイヤ -> Date & Time API
  • 静的コード解析しないと眠れいない -> Type Annotation
  • なにはなくとも GUI -> JavaFX
  • サーバーサイド JS に興味あり -> Nashorn

そこそこ満足していただいたようで、ほんとよかったです。

中でも、for 文禁止が妙にうけてしまったのですが、ほんとは別に禁止しなくてもいいと思っていますし、for 文で書いた方が分かりやすい場合もあると思います。

ただ、はじめは強制しないと、メンバが安易に流れて、Stream で書かないわけです。で、メンバ全員がちゃんと Stream で書けるようになるまで禁止にしたというわけです。

ただ、forEach メソッドは消せないんですよね。なるべく、forEach メソッドに書く分量を減らすようにはいっているのですが...

 

さて、今回の CCC、過去最高の参加者になりました! ほんとありがとうございます。今回は、Java SE 8 特需だとは思いますが、それでも多くの人に参加していただいたのはうれしいです。

また、今回は半数が初参加の人でした。JJUG としては、今回参加していただいた人たちが次回も参加していただけるようにしていかなければと思ってます!!

2013/12/11

大都会岡山 Advent Calendar 11 日目 - 岡山といえばパフェだよね #2

このエントリーをはてなブックマークに追加
_DSC6753

このエントリは 大都会岡山 Advent Calendar 2013 の第 11 日目のエントリーです。

昨日は @ne_sachirou さんの 岡山に帰ろう で、明日は Tama Eguchi さんです。

 

去年も、フルーツパフェの街 岡山の紹介をしたのですが、こりもせず今年も。ということで、今年、櫻庭が岡山で食べたパフェを紹介しようと思います。

カフェレストランオリビエ

今年、櫻庭が岡山にいったのは岡山 Java ユーザ会の Java Day Tokyo 2013 報告会@岡山 に参加するためです。報告会は 6/15 だったのですが、櫻庭は前泊して 6/14 に岡山入りしました。

岡山に着いたのは、夜の 9 時ぐらい。その時間でまだパフェを出してくれていて、駅のそばといえば、JR 西日本が運営しているホテルグランヴィアのカフェのオリビエです。

去年もここで食べましたけど、やっぱり一番安定していていいですね。

今年、食べたのは岡山県産足守メロンのパフェ。メロンがふんだんに使われていて、豪華ですね。

ただ、グラスの一番下にコーンフレークを使っているのがダメ。パフェの最後にコーンフレークなどを食べさせられる不幸を考えてみてください!!

_DSC6779

_DSC6757

_DSC6762

関連ランキング:カフェ | 岡山駅前駅岡山駅西川緑道公園駅

Antenna

続いて、城下にあるカフェの Antenna でいづし大正浪漫パフェ。

報告会の前のランチに来たわけですが、ランチで食べたスリランカのフィッシュカレーはおいしかったのですが....

パフェの要素ってだいたい決まっていて、フルーツ、アイスクリーム、生クリームが 3 大要素だと思います。この中で一番おいしさがはっきり分かれるのが生クリーム。

フルーツは新鮮であれば、それほど外すことはないですし、アイスクリームもどうせ市販のものが多いはずなのでそれほど差は表れません。

ところが、生クリームはすごいよく分かるんですよね。

ここで使われているのは、たぶん業務用の冷凍生クリーム。これがまたおいしくないんですよ。他にもエスプーマで作った生クリームもおいしくありません。エスプーマはスタバで使っているやつです。

なるべく手をかけたくないというのは分かるんですけど、ここまで手を抜かなくてもいいんじゃないかなぁ。少なくとも看板メニューにしているわけなんだし。

ということで、残念なパフェでした。

_DSC6968

_DSC6964

_DSC6979

_DSC6960

_DSC6943

スリランカフィッシュカレー

_DSC6944

_DSC6950

パパドもついてました

関連ランキング:カフェ | 城下駅柳川駅県庁通り駅


コンテンツカフェ

報告会の後、打ち上げから帰ってきても、まだ日が沈んでません! ということで、もう 1 つパフェです。

天満屋のそばのコンテンツカフェでピオーネのパフェ。

ところが、ここも冷凍生クリーム。この日食べた 2 つのパフェはどちらも外してしまいました。残念。

_DSC7157

_DSC7150

_DSC7169

関連ランキング:カフェ | 城下駅県庁通り駅郵便局前駅



クラブラティエ

クラブラティエは岡山珈琲館が経営している、パン屋を併設しているカフェです。

昨日食べたパフェが 2 つとも残念だったので、最後にもう 1 つ食べていこうと思い、岡山駅のそばのここに来てみたのでした。

食べたのは夏のフルーツパフェ。でも、パフェには全然期待していませんでした。だって、珈琲館ですよww

ところが、これがおいしかった!!

生クリームもちゃんとしたのつかっていたし、フルーツもおいしかったのです。マンゴーが冷凍物なのがちょっと残念ですけど、まぁ許容範囲。

しかも、一番下はコーンフレークではなくて、ジュレなのもポイントが高いです。

何ごともあなどってはいけないですね ^ ^;;

_DSC8103

_DSC8096

_DSC8115

関連ランキング:喫茶店 | 岡山駅前駅西川緑道公園駅岡山駅





番外編 #1 フリュティエ

岡山のフルーツパフェの Web にパフェの一覧があるのですが、そこに出ていたのでフリュティエ。ここではさんすての中にあり、テイクアウトのみのケーキ屋さんです。

Web に出ていたパフェはなかったので、普通のケーキ買ってきました。

_DSC6796

_DSC6788

_DSC6801
_DSC6839

_DSC6812

_DSC6865
_DSC6881

関連ランキング:洋菓子(その他) | 岡山駅岡山駅前駅西川緑道公園駅




番外編 #2 白十字 一番街店

こちらも Web に出ているので来てみたのですが、パフェがない! 完全に自分が勘違いしていたのですが、パフェがあるのは白十字 今店で一番街店ではなかったのです ><

それでも、ケーキは食べていくのです。

_DSC6926

_DSC6936

_DSC6921

関連ランキング:ケーキ | 岡山駅前駅岡山駅西川緑道公園駅





番外編 #3 さくら Cafe

もうパフェも岡山も関係ないのですが、尾道で食べた和菓子がおいしかったので、いっしょに貼っておきます ^ ^;;

_DSC7918

_DSC7940

_DSC7970

関連ランキング:喫茶店 | 尾道駅


おまけ

ほんとについでなのですが、尾道でみつけた子猫が超絶かわいかったので、貼っておきます ^ ^;;;;

_DSC7352

来年は第 3 弾やりたいので、ぜひ岡山によんでください!!

2013/12/09

Java Advent Calendar 9 日目 - Stream のパラレル処理

このエントリーをはてなブックマークに追加
このエントリは Java Advent Calendar 2013 の第 9 日目です。

昨日は、@nabedge さんの Mixer2のSpringMVC連携機能がver 1.2.17でさらに進化!
明日は Satoyuki Tsukano さんです。

去年も一昨年も Project Lambda について書いてきました。一昨年は Project Lambda の基本的なところ (まだこの頃は Stream がありませんでした)、去年は Stream の遅延処理についてです。

今年はこの Advent Calendar でも Project Lambda や Stream を扱っている人がいるぐらい、興味を持っている人が増えてきたと思います。

普通の Stream の使い方とかは、当たり前なので、ちょっと違う視点から書いてみようと思います (普通の Stream の使い方は ITpro の Java 技術最前線にいつか書きます)。
で、何を取り上げるかというと、パラレル処理です。去年はシリアルの方をやったので、残っていたほうです。

Stream のパラレル処理は Java SE 7 で導入された Fork/Join Framework がベースになっています。でも、どうやって Fork/Join Framework を使っているのかがよく分からないので、そこら辺を調べてみるということです。

ここでは、Fork/Join Framework がどうやってパラレル処理をするかまでは立ち入らずに、Stream がどのように Fork/Join Framework を使っているかだけにとどめておきます。
で、解析するコードはこちらです。

        IntStream.range(0, 11)
                 .parallel()
                 .map(x -> x*2)
                 .reduce(0, (x, y) -> x+y);

0 から 10 までの整数の 2 乗和を求める処理です。
では、順番に処理を追っていきましょう。
  1. IntStream.range
  2. parallel
  3. map
  4. reduce
なお、解析には JDK 8b118 を使用しています。今後、コードが変化する可能性もあるので、ご了承ください。

というのも、去年使用した b64 と比較すると、実装がかなり変わっているからです。ここが違うということはいいませんけど、かなり変わっていました。これから変更される可能性はすくないですが、テストの過程で若干変化する可能性はあります。

IntStream.range メソッド

普通の Stream は Iterator インタフェースと同じように、ソースがあってそこから生成するのですが、いくつかそれとは違う生成法もあります。IntStream.range メソッドもそんなメソッドのうちの 1 つです。

そういえば、Stream と何も書いていない時は、Stream インタフェースと IntStream インタフェースなどのプリミティブ用 Stream を合わせた言い方にしています。ここのインタフェースを指す時は Stream インタフェースと記述し、オブジェクトであれば Stream オブジェクトと書きます。

ということで、Stream にはオブジェクト用の Stream インタフェースと、プリミティブ用の IntStream/LongStream/DoubleStream インタフェースがあるわけです。この中で IntStream インタフェースと LongStream インタフェースだけ range メソッドが定義されています。

range メソッドは引数が 2 つで、1 つ目が Stream がはじまるはじめの数、2 つ目が最後の数の 1 つ大きい数です。上のように range(0, 11) と書くと、0 から 10 までの IntStream オブジェクトが生成されます。

2 つ目の引数を含めたい時は rangeClosed を使います。

この range メソッドを使えば、for (int i = 0; i < 11; i++) { ... } のようなループを Stream で書き換えることができるはずです。

まぁ、それはいいとして、range メソッドの実装です。

    public static IntStream range(int startInclusive, int endExclusive) {
        if (startInclusive >= endExclusive) {
            return empty();
        } else {
            return StreamSupport.intStream(
                    new Streams.RangeIntSpliterator(startInclusive, 
                                                    endExclusive,
                                                    false),
                                                    false);
        }
    }

RangeIntSpliterator オブジェクトを作成して、StreamSupport.intStream メソッドをコールしています。

そういえば、Project Lambda ではインタフェースにデフォルトメソッドを書けるようになりましたが、static メソッドも書けるようになったのでした。ここでも、それが使われています。

ここで重要なのが RangeIntSpliterator クラスです。RangeIntSpliterator クラスは java.util.Spliterator インタフェースの実装クラスです。

Spliterator インタフェースは分割のためのインタフェースで、Stream を使う時には必ず出てきます。とはいっても、表にはあまり出てこないインタフェースなので、意識はしないと思います。

ここでは、そういう Spliterator インタフェースの実装クラスを使っていたとところにとどめておきましょう。

StreamSupport.intStream メソッドに移りましょう。

    public static IntStream intStream(Spliterator.OfInt spliterator, 
                                      boolean parallel) {
        return new IntPipeline.Head<>(
            spliterator,
            StreamOpFlag.fromCharacteristics(spliterator),
            parallel);
    }

intStream メソッドでは、IntPipeline.Head オブジェクトを生成しています。IntPipeline.Head クラスのスーパークラスが IntPipeline クラスです。そして、この IntPipeline クラスが IntStream インタフェースの実体とでもいうクラスです。

そして、IntPipeline クラスには 3 種類の内部クラスがあります。その 1 つが Head クラスです。いうなれば Stream のパイプラインの先頭ということだと思います。

最後に Head クラスのコンストラクタの最後に parallel を指定できるようですが、ここでは false が設定されています。

ということで、range メソッドで IntPipeline.Head オブジェクトが作成されることが分かりました。

parallel メソッド

先ほど生成した IntPipeline.Head オブジェクトは parallel の指定がされていなかったので、paralle メソッドでパラレル対応にします。とはいっても、パラレル処理用のクラスがあるわけではありません。

parallel メソッドは IntPipeline クラスではなく、そのスーパークラスの AbstractPipeline クラスで定義されています。

    public final S parallel() {
        sourceStage.parallel = true;
        return (S) this;
    }

単純に parallel 変数に true を代入しているだけでした。

map メソッド

続いて、map メソッドです。この map メソッドに関しては、去年の blog で紹介したのとはずいぶん変化しています。

    public final IntStream map(IntUnaryOperator mapper) {
        Objects.requireNonNull(mapper);
        return new StatelessOp<Integer>(
                this, StreamShape.INT_VALUE,
                StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
            @Override
            Sink<Integer> opWrapSink(int flags, Sink sink) {
                return new Sink.ChainedInt(sink) {
                    @Override
                    public void accept(int t) {
                        downstream.accept(mapper.applyAsInt(t));
                    }
                };
            }
        };
    }

去年の実装では ValuePipeline クラスを使用していたのですが、今は StatelessOp オブジェクトを生成して返しています。この StatelessOp クラスは、先ほどの Head クラスと同様に IntPipeline クラスの内部クラスで、サブクラスになっています。

Stream の中間のパイプラインのうち、ステートを持たないものが StatelessOp クラスになるようです。map メソッドは順番は特に保持しなくてもかまわないので、StatelessOp クラスが選択されているのだと思います。

クラス名は変化していますが、処理自体は Sink インタフェースの匿名クラスで記述されていおり、処理を遅延させられるようになっています。

ちなみに Sink インタフェースは、java.util.functions.Consumer インタフェースのサブインタフェースで、連なった処理を記述するためのインタフェースのようです。

accept メソッドで downstream の accept メソッドをコールしているのは去年と同じですね。

reduce メソッド

ここまではパラレルに関する実質的な処理は表れませんでした。パラレル処理があるのは、パイプラインの途中過程ではなく、最後の処理になります。

去年も forEach メソッドの中でパラレルかどうかのフラグがあったところまでは見ていましたが、reduce でも同じような処理でパラレル化どうかを切り分けています。

では、reduce メソッドの実装を見ていきましょう。

    public final int reduce(int identity, IntBinaryOperator op) {
        return evaluate(ReduceOps.makeInt(identity, op));
    }

ReduceOps クラスは ReduceOp オブジェクトを生成するためのファクトリクラスです。ReduceOp クラスは TerminalOp インタフェースの実装クラスで、パイプラインの最後の処理を表しています。
とりあえず、ReduceOps.makeInt メソッドを見てみましょう。

    public static TerminalOp<Integer, Integer>
    makeInt(int identity, IntBinaryOperator operator) {
        Objects.requireNonNull(operator);
        class ReducingSink
                implements AccumulatingSink<Integer,
                           Integer, ReducingSink>, 
                           Sink.OfInt {
            private int state;

            @Override
            public void begin(long size) {
                state = identity;
            }

            @Override
            public void accept(int t) {
                state = operator.applyAsInt(state, t);
            }

            @Override
            public Integer get() {
                return state;
            }

            @Override
            public void combine(ReducingSink other) {
                accept(other.state);
            }
        }
        return new ReduceOp<Integer, Integer, 
                             ReducingSink>(StreamShape.INT_VALUE) {
            @Override
            public ReducingSink makeSink() {
                return new ReducingSink();
            }
        };
    }

makeInt メソッドの中で ReducingSink クラスを定義しています。このクラスも Sink インタフェースの実装クラスです。とりあえず、combine メソッドと accept メソッドがあったということだけ覚えておきましょう。

そして、この ReducingSink オブジェクトを生成する makeSink メソッドを定義した ReduceOp オブジェクトを返しています。

では、再び IntPipeline クラスの reduce メソッドに戻って、evaluate メソッドを追ってみましょう。このメソッドは IntPipeline クラスではなく、スーパークラスの AbstractPipeline クラスで定義されています。

    final <R> R evaluate(TerminalOp<E_OUT, R> terminalOp) {
        assert getOutputShape() == terminalOp.inputShape();
        if (linkedOrConsumed)
            throw new IllegalStateException(MSG_STREAM_LINKED);
        linkedOrConsumed = true;

        return isParallel()
               ? terminalOp.evaluateParallel(
                     this, sourceSpliterator(terminalOp.getOpFlags()))
               : terminalOp.evaluateSequential(
                     this, sourceSpliterator(terminalOp.getOpFlags()));
    }

ここでやっとパラレルかどうかで振り分けを行っています。

evaluateParallel メソッドの引数で sourceSpliterator メソッドがコールされていますが、この戻り値は IntStream.range メソッドで使用した RangeIntSpliterator オブジェクトになります。ようするに、自分自身と Spliterator オブジェクトを引数にして、evaluateParallel メソッドをコールしているわけです。

この terminalOp 変数は、さきほど作成した ReduceOp オブジェクトを指しています。

        public <P_IN> R evaluateParallel(PipelineHelper<T> helper,
                                         Spliterator<P_IN> spliterator) {
            return new ReduceTask<>(this, helper, spliterator).invoke().get();
        }

evaluateParallel メソッドでは、ReduceTask オブジェクトを生成しています。コンストラクタの引数の this が ReduceOp オブジェクトで、helper 変数が IntPipeline オブジェクト、spliterator 変数が RangeIntSpliterator オブジェクトです。

なんたら Task というクラスが出てくると、なんとなくパラレルっぽいですね。

それもそのはず、ForkJoinTask <- CountedCompleter <- AbstractTask <- ReduceTask というクラス構成になっていて、Fork/Join Framework がようやく出てきました。

ちなみに、ReduceTask クラスは ReduceOps クラスの内部クラスになっていて、ほとんどの処理は AbstractTask クラスで行っているようです。

ReduceTask オブジェクトが生成できたら、invoke メソッドをコールします。invoke メソッドを定義しているのは ForkJoinTask クラスです。

    public final V invoke() {
        int s;
        if ((s = doInvoke() & DONE_MASK) != NORMAL)
            reportException(s);
        return getRawResult();
    }

if 文の中に入ってしまってますけど、重要なのは doInvoke メソッドです。doInvoke メソッドも ForkJoinTask クラスで定義されています。

    private int doInvoke() {
        int s; Thread t; ForkJoinWorkerThread wt;
        return (s = doExec()) < 0 ? s :
            ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) ?
            (wt = (ForkJoinWorkerThread)t).pool.awaitJoin(wt.workQueue, this) :
            externalAwaitDone();
    }

処理を行っているのは、doExec メソッドです。doExec メソッドも ForkJoinTask に定義されています。

    final int doExec() {
        int s; boolean completed;
        if ((s = status) >= 0) {
            try {
                completed = exec();
            } catch (Throwable rex) {
                return setExceptionalCompletion(rex);
            }
            if (completed)
                s = setCompletion(NORMAL);
        }
        return s;
    }

そして、処理は exec メソッドに飛ばされます。exec メソッドは CountedCompleter クラスで定義されています。

    protected final boolean exec() {
        compute();
        return false;
    }

で、compute メソッドに委譲されています。ついでに、戻り値は false なので、先ほどの doExec メソッドの if (completed) ... は実行されないことがわかります。

そして、compute メソッドを定義しているのは AbstractTask クラスです。

    public void compute() {
        Spliterator<P_IN> rs = spliterator, ls; // right, left spliterators
        long sizeEstimate = rs.estimateSize();
        long sizeThreshold = getTargetSize(sizeEstimate);
        boolean forkRight = false;
        @SuppressWarnings("unchecked") K task = (K) this;
        while (sizeEstimate > sizeThreshold
               && (ls = rs.trySplit()) != null) {
            K leftChild, rightChild, taskToFork;
            task.leftChild  = leftChild = task.makeChild(ls);
            task.rightChild = rightChild = task.makeChild(rs);
            task.setPendingCount(1);
            if (forkRight) {
                forkRight = false;
                rs = ls;
                task = leftChild;
                taskToFork = rightChild;
            }
            else {
                forkRight = true;
                task = rightChild;
                taskToFork = leftChild;
            }
            taskToFork.fork();
            sizeEstimate = rs.estimateSize();
        }
        task.setLocalResult(task.doLeaf());
        task.tryComplete();
    }

やっと、Fork/Join Framework っぽい書き方が出てきました。

Fork/Join Framework は分割統治法で、タスクを分割し、分割したタスクを fork していくという処理になります。このタスクを分割するという処に Spliterator が使われているわけです。

RangeIntSpliterator クラスの場合、単純にはじめと終わりの半分のところで分割しています。そして、makeChild メソッドでタスクを分割していますが、ReduceTask クラスの場合、単純に新しい ReduceTask オブジェクトを生成しているだけです。

ReduceTask オブジェクトが対象とする範囲は Spliterator オブジェクトが持っているので、これで大丈夫なわけです。

そして、左、右の順番で fork されています。fork するということは、パラレルに処理されることになります。fork すると、そのタスクの compute メソッドがコールされるので、再帰的にタスクを分割して処理できます。

タスクが十分に小さくなった段階で、doLeaf メソッドがコールされます。ReduceTask クラスでの doLeaf メソッドは次のようになります。

        protected S doLeaf() {
            return helper.wrapAndCopyInto(op.makeSink(), spliterator);
        }

先ほど ReduceOps クラスの makeInt メソッドで定義した ReduceOp クラスの makeSink メソッドがコールされているので、ReduceSink オブジェクトが生成されて、引数になります。

wrapAndCopyInto メソッドは AbstractPipeline クラスで定義されています。ちょっとはしょりますが、wrapAndCopyInto メソッドは内部的に copyInto メソッドをコールしています。

    final <P_IN> void copyInto(Sink<P_IN> wrappedSink, 
                                     Spliterator<P_IN> spliterator) {
        Objects.requireNonNull(wrappedSink);

        if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
            wrappedSink.begin(spliterator.getExactSizeIfKnown());
            spliterator.forEachRemaining(wrappedSink);
            wrappedSink.end();
        }
        else {
            copyIntoWithCancel(wrappedSink, spliterator);
        }
    }

この中の Spliterator オブジェクトの forEachRemaining メソッドがパイプラインをさかのぼって、処理を行う部分です。この場合だと、map メソッドを行ってから、reduce メソッドを行います。

では、RangeIntSpliterator クラスの forEachRemaining メソッドを見てみましょう。

       public void forEachRemaining(IntConsumer consumer) {
            Objects.requireNonNull(consumer);

            int i = from;
            final int hUpTo = upTo;
            int hLast = last;
            from = upTo;
            last = 0;
            while (i < hUpTo) {
                consumer.accept(i++);
            }
            if (hLast > 0) {
                // Last element of closed range
                consumer.accept(i);
            }
        }

やっと、accept メソッドが出てきました。ここで、まず map メソッドで作成した StatelessOp オブジェクトの accept メソッドがコールされ、そしてその後 ReducingSink クラスの accept メソッドがコールされるわけです。

ところで、map メソッドはパラレル処理でしやすいですけど、reduce メソッドはどうするのか思いますよね。

タスクを分割した最後のところは 0 と IntStream オブジェクトの個々の要素の map メソッドの戻り値を足し合わせます。そして、forEachRemaining メソッドを抜けて、compute メソッドまで戻ります。
そして、compute メソッドの最後の task.tryComplete() が実行されます。

ReduceTask クラスの onCompletion メソッドは次のようになっています。

        public void onCompletion(CountedCompleter<?> caller) {
            if (!isLeaf()) {
                S leftResult = leftChild.getLocalResult();
                leftResult.combine(rightChild.getLocalResult());
                setLocalResult(leftResult);
            }
            // GC spliterator, left and right child
            super.onCompletion(caller);
        }

leftResult 変数の結果と rightChild 変数の結果を combine メソッドで統合しています。この combine メソッドは ReducingSink の combine メソッドです。先ほど ReducingSink クラスは示しましたけど、もう一回出します。

            public void combine(ReducingSink other) {
                state = combiner.apply(state, other.state);
            }

combiner 変数の型は BinaryOperator インタフェースです。つまり、Lambda 式で記述している部分は、BinaryOperator インタフェースの apply メソッドなわけです。つまりここでは、単純に足し算をしています。

ということで、0 と map メソッドの戻り値を足した右と左のタスクが、その上の段階で足し合わされるわけです。これがさらに統合されて、最終的にすべての足し算が行われるというわけです。

というわけで、分割統治でパラレルに reduce メソッドを行っていく処理を追ってみました。

今回は reduce メソッドで見てきましたが、他のメソッドではまた違う方法でパラレルに処理しています。特に順番が決まっている処理の場合、パラレル処理をするための工夫があるので、もし興味があるようでしたらぜひ見てみてください。

ふー、長かった (笑)。

2013/09/27

Napa へ

このエントリーをはてなブックマークに追加

帰国するのは日曜日なので、今日と明日はオフ。

ということで、去年にひきつづき、車で Napa へ。今年は大山さん、谷本さん、高山さんと私の 4 人。

今年は行きは大山さんの運転。

今年も Robert Mondavi -> Rutherford Hill へ。

大山さんはいつものごとく Rutherford Hill でお買い物。上の写真が Rutherford Hill Winery です。谷本さんはお酒飲めないのですが、お土産用に買うのだとここで同じようにワイン購入。

まだランチの予約時間までちょっと時間があったので、Rutherford Hill のそばの Rutherford Ranch へ。

でも、ここはおいしくなかったらしいです。大山さん、高山さんが試飲している時は暇なので、2 人の写真を撮っていたら店員さんにパパラッチなのかといわれてしまいました ><











Robert Mondave Winery

ワインのラベルと同じ画角で撮ってみたのですが、どうでしょう?










Rutherford Hill Winery



何を運んでいるかと思ったら



ブドウの絞りかすでした





Rutherford Ranch Winery




Auberge du Soleil

このエントリーをはてなブックマークに追加

今年もランチは Auberge du Soleil へ。

他にも行ってみたいところはあるのですが、Auberge du Soleil は景色もいいので、ついこちらを選んでしまいます。といっても、スーツ着なくちゃいけないところは、行かないと思うけどww

天気がいいので、ほんといい景色です。

今年は前菜に Kona Kampach Crudo、メインは Wolfe Ranch Quail、デザートに Gravenstein Apple Tart です。

Kona Kampach Crudo の Kampach はカンパチのこと。まさか寿司だとは ^ ^;;

メインの Quail はウズラのこと。ウズラおいしかった。皮はパリッとして、中はジューシー。アメリカは鶏肉は胸肉なのでぱさぱさしがちなのですが、これはおいしかった。

デザートの Apple Tart はハズした >< 以前もこれ頼んでハズしたと思ったことがあったのに、学習能力がまったくありません。困ったもんだ。














Sonoma

このエントリーをはてなブックマークに追加

ランチの後、微妙に時間があいてしまったので、Sonoma へ行ってみました。中心街をブラブラと。もうちょっと時間があったらよかったなぁ。


















Buckhorn Grill

このエントリーをはてなブックマークに追加

SF に帰ってきてから夕飯。みな全然おなかが減っていないというので、軽く Westfield の地下のフードコートへ。

私は Buckhorn Grill の Philly Buck。いわゆるチーズステーキです。

チーズステーキって、日本でもいけると思うんですけど、全然店がないんですよね。なんでなんだろう?

今日のおやつ - Cafe Madeleine

このエントリーをはてなブックマークに追加

今日も Walgreens で Cafe Madeleine のケーキ購入。今年は Cafe Madeleine の店にはいってないけど、よく食べましたww

フレッシュフルーツタルトとオペラ。というか、なんでこれがオペラなの? という感じ。

タルトには土台としてチョコが使われているんだけど、アーモンドプードルの層はないの?

まぁ、いいんだけどね。.






2013/09/26

JavaOne 2013 SF 最終日

このエントリーをはてなブックマークに追加

長いようで短かった JavaOne も今日でおしまいです。今日は朝から Communitiy Keynote。今年はちゃんと起きて、聞きに行くことができました (笑)。

今年も、朝ご飯に出会えたのが最終日だけ。毎年思うのですが、朝ご飯ははじめのセッションの前に出してほしい!!!! #てらだよしおがんばれ といえば、改善するかなぁ?

さて、今日聴講したのは、以下のセッションです。

ほんとは、後 2 つほどセッションをとるつもりだったのですが、Keynote 直後の JVM Advanced Tuning は Keynote が伸びて、満席で入れませんでした。もう 1 つ、Swing から JavaFX のマイグレーションに関するセッションもとるつもりだったのですが、こちらは昼ご飯を食べに行っていたら、間に合わなかったという ><

まぁ、最終日はこんなものです。

KEY11027 Java Community Keynote


JavaOne 最終日は Community Keynote が定番になってきました。

前半はスポンサーキーノートで Freescale の Geoff Less。初日の IBM の話に比べると、かなりおもしろいです。

だいたいスポンサーキーノートは聞いている方も適当に流すのですが、めずらしく今日のキーノートはみなちゃんと聞いていた感がありました。職に結びついた話だからかもしれませんが。






さて、Community Keynote です。

Community Keynote では次の 5 つのテーマについて、それぞれなにがしかの人が登壇して喋るという形式。

  • Education
  • Safety
  • Environment
  • Space
  • Oceans

一番はじめの Education は分かるものの、その他のトピックってどこがコミュニティに関係するんだろうというものばかりですね。最後の Space と Oceans なんて、かなりとってつけた感があります。

こんな感じなので、それほど盛り上がらず。

一番、盛り上がったのが 10 歳のプログラマの少年が壇上に上がった時。彼は Oracle のエバンジェリストの Arun Gupta の息子なのですが、Ecliipse を起動して「これが Eclipse です」というだけで盛り上がるのです。たぶん、言っている本人はなんで大人たちが Eclipse でこんなに大騒ぎしているかまったく理解できないでしょうねww

このときには、まさか JavaOne の後に Arun が辞めてしまうとは思いもよりませんでした。

そういえば、寺田さんが世界デビューで (昨日、セッションがあったので、本質的にはそちらが世界デビューですけど)、なんと T-Shirt 投げをしましたよ! 寺田さんが T-Shirt を投げている、すぐそばに座っていたのに、こっちには投げてくれなかった (泣)。

最後には、James Gosling が出てきて喋ったんですが、彼が喋ったのは初日にあった NetBeans のセッションと同じ内容 ><

Gosling がでてきたから、Oceans だったわけです。


















寺田佳央走る





寺田佳央投げる






































CON4904 Advancements in Text and Internationalization for JavaFX 8


スピーカは Oracle の Felipe Heldrich。JavaFX 8 の新機能である、リッチテキストに関するセッションです。

リッチテキストを著すための中心となるのが、Text クラス専門のコンテナである TextFlow クラスです。TextFlow クラスでは複数行の文字列を表示可能で、改行処理も常に行います。

TextFlow に貼る個々の Text オブジェクトのプロパティもしくは CSS を変化させておけば、それに応じたリッチテキストが表示できるというわけです。

同じような機能を持つコンテナとして FlowPane がありますが、TextFlow では改行を扱えることや、BIDI を扱うことができます。

ただし、表示するだけなので編集の機能はありません。

また、Text の wrappingWidth や textAlignment、x、y などのプロパティは無視されます。

TextFlow の描画にはネイティブのものと T2K の両方を扱えます。たとえば、ネイティブの例として、Windows では DirectWrite、OS Xでは CoreText が使用されます。Linux ではデフォルトで T2K になるそうです。

JavaFX 8 では BIDI が正式にサポートされ、文字列が左から右だけではなく、アラビア語のように右から左に書くことも可能になります。

どうやら、Internationalization といっていたのは、この BIDI のことだけのようです。なんだかなぁ....

というわけで、今年も JavaOne もおしまいです。

Hog Island Oyster Co.

このエントリーをはてなブックマークに追加

ランチは JavaOne では恒例の Ferry Building Marketplace にある Hog Island Oyster Co. へ。今年はここにくる時間がなかなか取れなくて、最終日になってしまいました。

歩いて行ったり、少し待ったりしたせいで、午後のセッションを 1 つすっ飛ばしたのはよくあることです ^ ^;;

やっぱり牡蠣おいしいよね。



























牡蠣食べている時にクマモトという牡蠣の話になって、料理にはいっぱい日本語が使われている話になったのです。キノコはシイタケもシメジもマイタケも使われているし。Ferry Building の中にはキノコやさんがあって、そこもシイタケとかそのまま売っているという話をしたら、誰もキノコ屋があるのを知らない。みんな何度も Ferry Building に来ているんじゃないの?

ということで、食後はキノコ屋へ。ほらシイタケもシメジもナメコもあるでしょ。


Ocean Beach 撮影会

このエントリーをはてなブックマークに追加

@yusuke が今年も蟹の前に走りに行くといっていたので、それに対抗するわけではないけど、Ocean Beach で日没撮影会。

蟹パーティの開始が後 30 分遅いともうちょっと余裕を持って撮れたんだけどなぁ...

まぁ、それでも完全に日没するところは撮れたからいいとしましょう。

































蟹パーティ 2013 @ Thanh Long

このエントリーをはてなブックマークに追加

恒例の蟹パーティ at Thanh Long。今年は 50 人越え。これ以上になると、2F を貸し切らないと入らないけど、それでも後 10 人ぐらいだよなぁ。来年はどうするんだろう?







































































2013/09/25

JavaOne 2013 SF 第4日目

このエントリーをはてなブックマークに追加

ランチがおいしくないのはいつものことなのですが、今年みながそろってまずいと言っているのがサンドウィッチについてくる豆料理。

レンティスやひよこ豆、フェイジャオンなどを使った煮込み料理なのですが (日によってレシピが違っています)、日本の豆料理とはかけ離れているからかもしれません。でも、アラビアなどで普通に食べられている料理なので、櫻庭にはそれほど違和感がないんですよね。というか意外においしいと思っていました。でも、食べ慣れていないものを食べると、そんなものなのかもしれませんね。

さて、今日は夕方から Treasure Island でライブがあるので、セッションも早じまいです。ということで、今日は BOF はなしで、以下の 4 セッションを聴講しました。

今日の最後の Date & Time API は、月曜日の Date & Time API のセッションとあまり変わらず... まぁ、そんなものか...

CON2624 Modeling, Texturing, and Lighting Mesh Geometry in JavaFX 3D

スピーカは John Yoon。この人は Keynote で紹介された 3D の Duke のチェスをモデリングした人らしいです。

といことで、あまり Java のコードは出てきません。外部のモデリングツールで作成したものを、どうやって Java に持ってくるかところが中心になります。このセッションでは Blender と Maya でデモをしていました。

ところで、今年の JavaOne は部屋が明るくしてあります。去年までは部屋をかなり暗くしていて、スピーカがいることは分かっても、細かい表情などは全然分からないぐらい。それが不評だったのか、今年は一転して明るくなりました。

一部のセッションでは、資料のコントラストが低くて見にくい部分もあったのですが、全般的にはよかったと思います。

ところが、このセッションだけは以前のようにまっくら。初日、2 日目とやってきて明るすぎと文句が出たのかと思いましたけど、全体を通しても暗かったのはこのセッションだけ。やっぱり、Blender や Maya の画面をよく見せるというためなのかもしれません。そのせいもあって、このセッションは写真なし。いちおう撮ったのですが、何が写っているのか判別するのが大変なぐらいだったのです。

ついでですけど、今年使用しているプロジェクタがとても静か。今までのはファンがかなりうるさかったのですが、ファンの音も気にしなければ全然気がつかないぐらい。いろんなところで技術革新は行われているんですね。

さて、本題に戻って。

まずはじめに、Java で 3D のモデルを書いていく方法から。頂点を指定して、テクスチャをマッピングして、表面を指定してとやっていくのですが、単純なオブジェクトでも結構大変。これで複雑なモデルを Java で書くのは考えられないよね、ということで Blender と Maya です。

Blender や Maya で作成したモデルは、Keynote でも使用していた 3D Viewer で読み込みます。

かれはモデリングを切り紙にたとえていっていました。切り紙って伝わらないと思うんだけど、大丈夫なのかなぁ。いちおう、切り紙と折り紙は違うものでと話していましたが。

切り紙にたとえたメインは U-V マッピング。確かにこれは切り紙かもしれません。

後半はコードが全然出ないで、Blender と Maya でどうやってモデルを作っていくかのチュートリアルになってしまいましたが、こういうのを見る機会がなかなかないので、個人的にはおもしろかったです。

 

CON7942 Java 8 Streams: Lambda in Top Gear

スピーカは Brian Goetz と Paul Sandoz。Brian Goetz のセッションは 3 つ目ですが、このセッションは Stream に絞ったものです。

それにしても、このセッションはすごい行列。やはりみなの関心が高いんですね。

コレクションなどに対する Aggregate Operation (集約的操作とでも訳すのでしょうか) には、歴史的にループが使われてきました。しかし、基本的にはシーケンシャルでスケールしません。

これに対する Java SE 8 の答えが Stream というわけです。

はじめの例として NY のバイヤーが売り上げた合計を求めるには、外部イテレータでは次のように記述してきました。

int sum = 0;
    for (Transaction t: txns) {
        if (t.getBuyer().getState().equals("NY")) {
            sum += t.getPrice();
        }
    }

これに対して、Stream では次のように記述します。

int sum = txns.stream()
                  .filter(t -> t.getBuyer().getState().equals("NY"))
                  .mapToInt(t -> t.getPrice())
                  .sum();

パラレルに処理するのであれば、stream() を parallelStream() に変更するだけです。

Brian は Stream の方が読みやすいというのですが、これは慣れないと読みにくいですね。慣れればなんともないですけど。

今までの手法と Stream を比較すると、今までのループはコレクションの個々の要素について扱っているのに対し、Stream ではデータセットに対して扱っているという違いがあります。

また、従来が how だったのに対し、Stream は what だといいます。

ここから Stream の内部構造に入っていきます。

先ほどの例だと、Stream に対して filter と map、sum が使われていました。しかし、中間の filter と map はその場では処理されません。最後の sum ですべの処理が行われます。

つまり、filter, map, sum で 3 回のループが回るのではなく、filter と map は処理が遅延され、最後の map の 1 回だけイテレーションされます。

このような Stream に対する処理の連なりを、Brian はパイプラインと呼んでいます。

各パイプラインはソースを持っています。ソースは Stream の生成の方法によって異なります。Collection に対して Stream を生成した場合は、Collection のコンクリートクラスが、たとえば ArrayList などがソースになります。

このソースによって、Stream の性質が決まります。たとえば、ArrayList であれば SIZED と ORDERED。つまり、サイズと順番があるというわけです。

中間のオペレーションは処理が遅延されます。このオペレーションによっては Stream の性質を変更します。たとえば、map は SIZED は保持しますが、SORTED などは保持されません。

そして、最後のオペレーションでソースをたどって処理を行っていきます。終端のオペレーションとなるのは reduce, collect, sum, min, forEach などがあります。

もし、Stream でステートを持つような処理を行っている場合は、reduce もしくは collect を使用してステートレスにします。

たとえば、トランザクションから Seller だけを新たなリストにしたい場合を考えます。

List<Person> sellers = new ArrayList<>();
    txns.maps(Txn::getSeller)
        .forEach(s -> sellers.add(s));

と記述すると、ステートを持ってしまいます。このコードは下のように書き換えます。

List<Person> sellers = txns.maps(Txn::getSeller)
                               .collect(Collectors.toList());

Collectors クラスには toList 以外にも groupingBy などのメソッドが定義されています。

後半は Paul Sandoz がパラレル化について。

Java SE 7 で導入された
Fork/Join は低レベルすぎて、なかなか使いどころが難しい。

そこで、ParallelStream が登場します。

ParallelStream を考えた場合、デターミニスティックかどうかが重要になります。つまり、以下のコードで得られた結果は同一にならなくてはなりません。

List s = ...;
    List r1 = s.stream().map(...).collect(Collectors.toList());
    List r2 = s.parallelStream().map(...).collect(Collectors.toList();
 
    assertEquals(r1, r2);

これに対して、以下の例はデターミニスティックである必要はありません。

Txn t1 = s.stream().filter(t -> t.getPrice() > 100).findAny();
    Txn t2 = s.parallelStream().filter(t -> t.getPrice() > 100).findAny();

もちろん、デターミニスティックな場合の方がパラレル処理が難しくなります。

Stream のパラレル処理は、Fork/Join をベースとして作られているので、分割統治法で処理されます。実際に、sum や reduce などがどのように処理されるのかを解説しました。

toArray などの出たーミニスティ行くな処理をする場合には、分割した個々の要素が対応するインデックスを保持しておき、結果用の Array の適切なインデックスに結果を書き写していくということを行います。

ここでパフォーマンスの話。

パラレル処理したとしても、すべての場合でパフォーマンスが向上するわけではありません。シーケンシャルの方が速いこともあります。

Stream の終端の処理のマージ処理が大変であったりすると、パフォーマンスが落ちます。また、Stream の性質にも影響されます。オートボクシングもパフォーマンスに影響を与えます。たとえば、プリミティブを使用するのであればプリミティブに対応した IntStream のような Stream を使うべきです。

8 スレッドの MacBook Pro で行ったマイクロベンチマークの場合、ArrayList と IntSraem では要素数が多くなればパフォーマンスが向上しましたが、LinkedList ではあまりパフォーマンスが向上しませんでした。

これはマイクロベンチマークなので、すべての場合に当てはまるわけではないのですが、このように Stream のソースによっても大きくパフォーマンスが変わるということだと思います。

最後に分割統治法を行う Spliterator について。Iterator のパラレル版というようなもので、split メソッドが追加されています。ArrayList などだけではなく、HashSet などでも Spliterator を使用することができます。この場合は、Key によって分割を行っていきます。

こういう話はやっぱりおもしろいですね。こういう話を聞けるのが JavaOne の醍醐味です。





CON5091 Java Flight Recorder Behind the Scenes


スピーカは Oracle の Staffan Larsen。

Flight Recorder はトレースとプロファイルを行うツールで、JVM の中に組み込まれています。Java SE 7u40 から正式に使用することができるようになりました。

Flight Recorder でキャプチャできるのは、GC, Synchronization, Compiler, CPU Usage, Exception, I/O などがあります。サンプリングがベースなのでオーバーヘッドも小さく、正確らしいです。

プロファイルもオンデマンドで行うことができ、Mission Control もしくはコマンドラインからプロファイルを開始できます。

Flight Recorder というと、GUI がカッコいいことばかりに目が向きますけど、内部もかなりすごいらしいです。

Flight Recorder を使用可能にするには、Java の起動オプションで -XX:+UnlockCommercialFeatures -XX:+FlightRecorder を指定します。実際に起動するには、起動オプションの -XX:StartFlightRecording=filename=<path>,duration=<time> を指定します。

もしくは jcmd からでも起動できます。

後半は、Flight Recorder の内部構成について。

Compiler や GC などから収集したデータは ThreadLocal のバッファに一時的に収集し、それを Global Buffer に移し、最終的にディスクに保存します。

内部的にはすべての情報はイベントとして保持されます。また、イベントは XML で保持されるようです。

メモリリークを起こさないようにするためにも、収集したデータはずっと保持するのではありません。これにはコンスタントプールを使用して、なるべく使用するメモリを減らすようにしています。たとえば、クラスやメソッド、スレッドはプールします。

おもしろいのがスタックトレースもプールすること。同じスタックトレースであれば、プールしておくということなのでしょうけど、そんなに需要があるんでしょうか?

このセッションは一緒に谷本さんも聴講していたのですが、彼は Flight Recorder 対抗の解析ツールを作っているので、かなり刺激になったようです。確かに、解析ツールについてもうちょっと理解していたら、もっとおもしろかったかもしれません。

Oracle Appliciation Event - MIX

今日のメインイベント。


今年は、Maroon 5 と The Black Keys。

シャトルバスでヒルトンからトレジャーアイランドへ。バスの中ではみなと一緒なのですが、みなライブには興味がなくて、ライブに向かうのはいつも 1 人。でも、今年は北野さんが Maroon 5 の大ファンだということで、2 人でライブへ。

いつものごとく、一般人が入れる一番前に陣取ります。一番前は VIP たちのエリアになっているのもいつも通り。

MC の人が、今日 America's Cup で Oracle Team が優勝したと告げると、観客から USA コールが。

The Black Keys からなのかと思っていたら、いきなり Maroon 5。

Maroon 5 はベテランということもあって、やっぱりステージワークうまいですね。選曲は最新アルバムが一番の多いのですが、デビューのころからのヒット曲はもれなくやってました。ヒット曲入れると盛り上がるんですよね。

北野さんは最近聞き出したらしく、最近のヒットの Moves Like Jagger がお気に入りのようなんですが、私はやっぱり 1st の This Love か Sunday Morning ですね。もちろん、この 3 曲とも演奏してくれました。

そういえば、ボーカルの Adam は歌っている時のバリエーションが少ない。しかもいつも目をつぶってしまっています。写真を撮っているといつも同じような写真になっちゃうんですよね ^ ^;;









































Maroon 5 のライブの後、とりあえず食べ物のところへ。そこで、北野さんとはぐれてしまいました。すごい人数だからちょっと目をはなすとすぐに分からなくなってしまいます。

で、続いて The Black Keys。彼らはアルバムは持っているものの、そこまで聞き込んではいませんでした。

The Black Keys って 2 人組なのですが、おもしろいことにギターとドラム。CD だとそれほどでもないのですが、かなり重低音サウンド。それを担っているのが、ドラムの Patrick Carney。なんとダブルバスタム。ダブルバスドラはなんども見たことありますけど、ダブルバスタブははじめて、スネアよりもバスタムの方を多用しているので、重低音のサウンドになっているのでした。

そして、ギターがまた歪んだいい音を出しています。CD だとこれが分からないのですが、ライブだといいですよ、このバンド。

ところで、この 2 人、立ち位置がかなり離れているのです。ギターの Dan Auerbach を撮ろうとすると、ドラムの Patrick Carney がフレームアウトしてしまうのです。

しかも、Dan を撮りやすい位置だと、Patrick はシンバルで顔が見えなくなってしまうのです。で、はじめて一番前ではないところに移動。後半は後ろの方にある階段状のイス席で見たのでした。




















ライブが終わったのが 23:30 ぐらい。その後、ゲームのコーナーなどの写真を撮りつつ、まだ残っていた料理を食べたりして過ごしたのですが、ここで痛恨のデジカメのバッテリー切れ。すでに、12 時も過ぎていたので、すごすごと帰りました。

ホテルにもどったのは 1 時過ぎ。明日、起きられるか?