uehaj's blog

Grな日々 - GroovyとかGrailsとかElmとかRustとかHaskellとかReactとかFregeとかJavaとか -

速いぞGroovy!

Javaと同等レベルに高速に実行できるGroovyコードを書く方法が紹介されてます。
Yes, Fibonacci in Groovy can be as fast as Java !
こんなスクリプトです。

@ast.Bytecode
int fib(int i) {
 l0
    iload 1
    iconst_2
    if_icmpge l1
    iconst_1
    _goto l2
 l1
    frame SAME
    aload 0
    iload 1
    iconst_2
    isub
    invokevirtual '.fib','(I)I'
    aload 0
    iload 1
    iconst_1
    isub
    invokevirtual '.fib', '(I)I'
    iadd
 l2
   frame same1,'I'
    ireturn
}

int groovyFib(int i) { i<2?1:groovyFib(i-2)+groovyFib(i-1)}
println "Pure Groovy"
long sd = System.currentTimeMillis()
println groovyFib(40)
println "Computed in ${(System.currentTimeMillis()-sd)}ms"

println "Bytecode Groovy"
sd = System.currentTimeMillis()
println fib(40)
println "Computed in ${(System.currentTimeMillis()-sd)}ms"

あがが。
あごがはずれるわい。
Groovyで表現されるJava Bytecode DSLですね。
AST変換でバイトコード生成し、実行します。

余談ですが、Groovy 1.8では、プリミティブの演算が相当速くなるようですね〜。たのしみ。

HtmlUnit賛歌

知っている人には、今さらですが、HtmlUnitというものがあります。名前からするとxUnit系の、ユニットテストツールのような気がするかもしれませんが、違うよ!全然違います。誤解を招く名称です*1

これはGUIレスなブラウザなのです。GUIがないだけでなく、CUIコマンドラインインターフェースもありません。唯一できるのは、プログラムからブラウザの機能をJavaAPIを通じてJava(やGroovy)から呼び出し、そのAPIメソッド呼出しの結果を得るだけです。「非対話型ブラウザ」です。

いままで、XmlParserやNekoHtmlやHttpBuilderといったもののみでスクレイピングをしてきた諸兄は、HtmlUnitを一回使ってみるのがよろしいと思いました*2HtmlUnitを使うことで、ログイン処理や、ページ遷移が簡単になったり、ページの表示にJavaScriptとかを使うケースも対応できたりとか、それらを使ってちまちまやるよりも圧倒的に簡単になることがあります。プロトコルレベルでやるってことは、基盤となるブラウザ機能の必要なところは自前で作らないといけないってことですから。ブラウザの実装であるHtmlUnitは、そこらへんJavaScriptを含めて実装済みになるので、楽なのです。

FireFoxをお使いなら、こちらHtmlUnitScripterHtmlUnitのコード断片を生成させるのも良しでしょう。

HtmlUnitでは、XPathを使う*3ことができるのですが、これも気に入りました。XPathはそれ自体汎用的で強力であるだけでなく、ブラウザと連携する他のツール(AutoPager Add-on(Chrome用もある)とかWebTest Recorder Sidebarでも使える)もあるのが強みです。

そんなこんなしてHtmlUnitを使って、

などを、JGGUG合宿の企画g100ponのリモート参加ネタとして上げさせていただきました。サンプルとして御笑覧下さい。なおリモート参加にあたり、規定コメントの不足やtwtterの送信を怠るなど、各位には失礼をば致しました。

*1:もちろんWeb機能テストで使えますが、そんなこと言ったら足し算だってユニットテストで使えます。

*2:HtmlUnitは下位層ではNekoHtmlを呼び出しています。

*3:HtmlPage#getByXPath()

スプーンとAST変換

さっき書いたenumの記事で思い出したのでご紹介ですが、フランスのINRIAがやってる(やってた?)Spoonというプロジェクトがありまして、これはなにかというと、Javaコンパイラを拡張して、特定アノテーションの指定をきっかけとしてコンパイラのコード生成とかに介入し、デザインパターンの実現をはじめとして、さまざまに機能拡張できるというものです。

これはなかなかGroovyのAST変換に近しいものです。Java版のAST変換というべきか、時期的に言ったらSpoonの方が遥かに先ですから、SpoonをGroovy流にしたのがAST変換というべきか。

Spoonを使ったプログラミングの例についてはこちらにありますが、さっきのN-tonだとか、分散計算だとかが実現されてます。DelegateGroovyに同名のがありやすね。使い方はちょっとちがうみたいですが。

実用性が高そうなものとしてはVisitorというのもあって、Visitorパターンで必要なさまざまなハウスキーピングメソッドを生成してくれるらしいのですが、GroovyのAST変換でも欲しい気がします。

ビューティフル・アーキテクチャ

ビューティフルアーキテクチャ

オライリージャパン
売り上げランキング: 87474
おすすめ度の平均: 4.0
4 1章が価値高い

この本買ってみました。
おもしろすぐるです。

たかだか、この十数年だと思うのですが、WEBやインターネットの隆盛の元で「3階層アーキテクチャ」「MVC2」といったWebアプリケーションを中心としたソフトウェアアーキテクチャが非常な人気を得ることとなり、普及してきています。

この状況下で、特にSIerとよばれるソフトウェア開発企業群は、Webアプリケーションを開発するための、組織としての効率化/自己最適化を行った結果、本来は多様であるはずのアーキテクチャを鍛えて練り上げ、作り上げる力を失ってきている場合もあるんじゃないでしょうか。ひょっとすると、「なんでもWebシステムにしておけ」的な雰囲気もある気がしています。この傾向は、Java開発をメインとする企業では特にそうかもしれません。イントラネット向け業務システムの開発が多い場合は特にそうかもしれません。OSSフレームワークを活用しようとすればするほどそうかもしれません。二次請け、三次請けと言った多階層の発注構造の元ではさらに特にそうかもしれません。オフショア開発とかだとさらに拍車がかかるかもしれません。短期開発だと(〃)受託開発だと(以下略)

効率化/コスト削減の美名の元になされた(それ自体は経済合理性の問題として間違いとは言えない)それらの数々の選択によって、アーキテクチャの多様さの可能性を捨て、同種同様のシステムを低スキルのメンバで受託開発し低コスト化を期するような開発組織体質を目指し過ぎてしまってたかもしれません。

しかしながら、本来、用途や目的に応じて多様なアーキテクチャが選択され構築されてきたのがソフトウェアの歴史上の常であり、むしろそれが本来の姿であり、個々の案件に特化した優れたアーキテクチャを発想し作りだす、という能力がエンジニアにとっておろそかにされてしまってきた傾向があったかもしれません(いや、どこが、とかじゃ無くて、あくまで一般論として・・。)。

Web+DBの3階層モデルもRDBMSも無期限に続くものでもないし、当然万能でもないし、そろそろほころびや限界を迎えつつある、と見てもおかしくありません。なのにそれに過剰に適応しすぎたことが、職業プログラマSIerと呼ばれる企業群の技術力を蝕み退化させる要素があったかもしれません。来るべきアーキテクチャ激動の時代を乗り越えるためには、アーキテクチャにフォーカスを置くべき。見直すべき。

とか熱い人なら言い出しかねぬ*1、この一冊。

いやこれはソフトウェア開発に愛着を持つ人なら目次見るだけでくらくらと脊髄反射で買うでしょうという本でした。読み終わるのがもったいない*2

目次からちょっと抜粋すると

  • 4章 メモリーを作る - Michael Nygard
  • 7章 Xenと仮想化の美 - Derek Murray/ Keir Fraser
  • 9章 JPC:ピュアJavax86 PCエミュレータ - Rhys Newman/ Christopher Dennis
  • 10章 Jikes RVM:メタサーキュラーな仮想マシンの威力 - Ian Rogers/ Dave Grove
  • 11章 GNU Emacs:漸進的機能追加方式が持つ力 - Jim Blandy

どうですか!(と言っても困ると思うけど)
はい、これだけで3千円出しますーと私は思った。安くていいですね。出版社も売れると見てるのでしょう。

*1:水を差すために皮肉な見方として言うと、これって「ソフト開発の神髄の一番面白いところ」を苦労無しでつまみ食いでエンターテイメントとして読めるってことなんですよね。「いいなーこういう開発したいなー」と思って読む。「タイガーウッズのプレーを見て喜ぶ一般ゴルファー」ってところか。そんな見方をすると悲しいけども、実際に役に立つ気が、する・・ような。

*2:実は、今日昼休みに買って、ちょい飛ばし読みで読んでまだ7章までしか読んでないのです。本当は読み終わってから感想書くべきなんでしょうが、経験上、読み終わってからだと、エントリ書く気力が薄れることが多いんですよね。読んでる途中の方が「この感想を誰かに伝えたい!!」という気分が高まる。だから読み終わる前に書いております。ご了承を。

Javaでクロージャを!!!

Javaでクロージャが使えない?

はぁ〜〜〜〜〜〜〜〜〜〜???
聞こえんなぁぁ〜〜〜〜〜〜???

といわんばかりの豪腕なライブラリ。

Closures support for Java using dynamic Groovy evaluation.

文字列を実行時にGroovyのクロージャに変換してるみたい。テストコードを見る限り、変数参照とかスコープ解決は、たぶんクロージャ引数と外部からstatic importとかさせたものだけが可能のようだ。

細粒度並列処理について

JSR-166yはJava 7で導入が予定されている、細粒度並列処理を効率よく行うことができるようにするためのライブラリ群です。Java7でjava.util.concurrent.forkjoinパッケージで提供される予定ですが、実は今でもダウンロードして利用できます。本記事ではこれが何のためのものでJava開発にどのようにかかわってくるか、を概要として解説します。JSR-166yの詳細およびfolk/joinアルゴリズムそのものについては解説しません。それらについては末尾の参考リンク先をどうぞ。

細粒度並行処理とは何か

new Thread()とかExecutorServiceでいちいち、どっこいしょ、と処理を始める(往々にしてその処理はプログラムが終了するまで続く)というのを祖粒度並行処理と呼びます。そうではなく、1回のソート処理を複数スレッドで実行するとか(並行ソート)、forループの繰り返し処理を1つのスレッドで逐次処理していくのではなく、繰り返しのそれぞれの段階を複数のスレッドで手分けして実行するとか、「一つの処理」の中で複数のスレッドをこまめに使いこなすというような処理です。たとえばgparsでいうと、

     final List result = [1, 2, 3, 4, 5].collectAsync {it * 2}
     // result == [2, 4, 6, 8, 10]

こんなかんじで、collectAsyncのところは非同期で複数のスレッドに分割されて実行されます。「息を吐くかのようにごく自然に並列処理」というわけです。

細粒度並行処理の意義

サーバ処理でスループットをあげるにはCPUを増やせばよいでしょう。ただし、この方法では個々のリクエストのレスポンスが短縮されるわけではありません。1度にたくさんのリクエストを処理できるようになるだけであり、個々のタスクの処理速度は向上しません(タスクが大量にあり、CPUが空きがなく、CPU空きの待ちが生じている場合には、CPUを追加することで待ち時間が減ることで処理時間が減りますが、いったん待ちが解消されたレベルでは、いくらCPUを増やしても処理時間がさらに向上することはありません)。

マルチコアのCPUでも同じことが言えます。アプリケーションやOSが適切にスレッドに分割配分されていなければ、コアがいくつ増えても無駄です。 CPUやコアを増やしても、同時に動作可能なタスク数が十分でなければ、コアが増えてもレスポンスはそれ以上は向上しません。簡略化した例で言うと、たとえばOSが1つ、アプリ1、アプリ2がそれぞれ1スレッドを使用しているとき、コアは3つまで増やせばレスポンスが向上しますが、あとは4つでも8つでも速度は同じです。

まとめると、コアやCPUが十分以上に利用できる環境では、それらを有効活用するために、いままで1リクエストのタスクで行っていた処理や、1スレッドでやっていた処理を、なんとかしてさらに細かいスレッドに分割して並列実行するようにする必要があります。性能向上という意味だけではなく、今まで必要だったマシン数が不要になり、無駄を省くことができるというケースもあるかもしれません。

背景として、現在の主力CPU開発では、1個1個のCPU自体の高速化(クロック向上とか)はいろいろな理由で頭打ち傾向なので、CPUメーカーはマルチコア化に力を注いでいるということが挙げられます。今後のCPUの性能向上を享受するためには、マルチコア(あるいはメニーコア)の活用を考える必要があります。

(追記:Tilera、100コアのCPUを発表という記事がありました)

並列化のチャンス

並列化を行う場所は、いくつか選択肢があります。

  • OSレベル
  • ライブラリ・データ構造レベル
  • フレームワークレベル
  • プログラミングレベル

細粒度並列化の場合、ライブラリ以下の部分になると思います。
JSR-166yはJavaプログラミングレベルでの解決策であり、比較的原始的なものです。これをつかって、細粒度並行処理を支援するフレームワークやライブラリ、データ構造を作っていくことは重要だし、実際にそのようにも使われていくと思われます。

ちなみに、 OSやフレームワークで、うまく並列化をするように隠蔽すれば、ユーザレベルのプログラミングでは並列化を意識しないでよい、という意見もあるかもしれませんし、それはそれでなされていくでしょう。実際、OSレベルとかはすでに結構並列化されています。「Giant Lock」とかで検索してみると良いでしょう。

ただ、並列化はそれなりにオーバーヘッドコストのかかる処理ですし、選択的に、意図を持って並列実行を指定できた方が効率が良い場合があります。また、ユーザコードが「ボトルネック」になっているならそこを解消しない限り全体のスケールも望めません。

それらのケースが、われわれが直接JSR-166yを使ってプログラミングすることになる動機です。しかし、JSR-166yはかなりプリミティブなレベルな部品なので、記述が煩雑になる可能性が高いです。Groovyではクロージャを使うことでその問題を改善できます。さらにGparallelizer改めgpars(Groovy Parallel System)は、並行処理のための高抽象レイヤ(データフロー、アクターモデル、並列コレクション処理)をGroovy DSLを駆使して提供するものであり、jsr166yライブラリを背後で使用しているものですが、これを使うことで記述が煩雑になることを抑えつつ、細粒度処理による性能向上を図ることができるでしょう。gparsについては別途語ることとします。

JSR-166yと従来のjava.util.concurrent.ExecutorServiceの違い

いずれも、スレッドプールを用いてスレッドを使いまわします。両者の違いは、JSR-166yはスレッドの切り替えに関して、細粒度タスク向けに最適化されていて、ExecutorServiceでは発生しうる待ち状態が極力発生しないようになっていて効率が優れています(「ワークスティーリング」と呼ばれる技法が用いられている)。また、JSR-166yはforkjoinという分割統治方による並列化サポートのアルゴリズムライブラリや、並列クエリ・集計を可能とするコレクションライブラリ「Paralell Array」などの機能を持っています。

なお、JSR-166yは細粒度タスクを効率よく実現する1ライブラリであり、これだけが細粒度タスクの実現方法ということではありません。

その他

関数型言語が注目を集めてるのも同じ背景ですね。関数型言語は並列実行に実に都合が良い性質があるからです。

Java 7における5つ(かそこいら)の変更点確定版

Java 7の最終変更点が確定したとのことで、記事を翻訳してみました。

Project Coin: The Final Five (Or So)

まず最初に、Project Coinへ興味深い提案をお送りいただいたみなさま、思慮深いコメントをくださった方々、そしてJavaプログラミング言語を発展せしめんと欲するまさしく活力のあるコミュニティの皆様にお礼申し上げます。

これ以上の波乱もなく、JDK7に含められることが決定した最終的なProject Coinの変更は以下の通りです。


相互作用が吟味されて、問題が明確になるまでは、これらの変更に対する仕様と実装、そしてテストは最終的なものにはならず、発展し続けるでしょう。これらのリストのアイテムのうちの2つは、送られてきた複数提案の組み合わせです。


整数リテラルの改善に関するオムニバス提案は、少なくとも「二進数リテラル」と「数値中の下線」を含みます。符号無しのリテラルをより良く扱うための方法も求められています。コレクションのための言語サポートは、コレクションのリテラルリストとマップに対する添字アクセスを含みます。技術的問題が解決されることが前提ですが。


最終リストでは受理しなかったけれども、さらなる検討を続けることになった2〜3の提案も残されています。


例外ハンドリングの改善は、言語に取っては良い変更ですが、型システムに対しての際どい冒険になるでしょう。なので、JDK 7に間に合うように十分に開発リソースを割けないだろう、と思っています。今後の検討で、言語を発展させるような、より優れた例外ハンドリングが再考されて欲しいと思っています。


エルビス演算子と関連する演算子はGroovyでは役に立つのですが、GroovyとJavaの違い、たとえばプリミティブ型の存在や、boxing/unboxingの相互作用は、Javaの場合にこれらの演算子をやや不便にしてしまうのです。Java 7は他のやり方、JSR308で可能となるnullチェックのようなやりかたで、nullハンドリングの苦痛を和らげてくれるでしょう。


32ビット範囲以上のエントリの大きさの集合型のネイティブサポートが今後ますます重要になっていくことに疑いはありません。コレクションに対する言語サポートは、プラットフォームが直接大量データを扱えるようになるまでは、その機能をライブラリとして開発できるようにします。


coin-devのメーリングリストは、受理された変更のリファインを続けられるように、利用可能なままにしておきます。選択されなかった提案に対するさらなる議論は、このメーリングリストではオフトピックとなります。


Project Coinの変更の実装の対部分は、2009年10月末までにJDK 7のMercurial開発リポジトリに入るはずです。


そのうちに、Project Coinの変更点をカバーする範囲は、1つのJSRとしてまとめるつもりです。

(2009-08-28 17:45:48.0)

エルビス演算子、セーフナビゲーションは入らなさそう。
「自動的なリソース管理」は、Groovyではクロージャを使ってやる、ローンパターンの代替だね。クロージャ無くなっちゃったからね。

Java7に関して、前に翻訳した記事もどうぞ。

(2009/9/2追記)コメント欄で指摘を受け、「型インターフェース」を「型推論」に修正しました。
(2009/9/2追記)(※注1)ダイアモンドとは、リンク先を読むと、ジェネリック型のnewで、変数の宣言にジェネリクス指定しておけば、右辺は<>(ダイヤ型)で済むということのようです。例:Map> anagrams = new HashMap<>()