Hatena::ブログ(Diary)

chimerastのエレガント指向プログラミング日記 このページをアンテナに追加 RSSフィード

2011-08-28

Scala会議に参加してきました「ScalaからGPUを使ってみる ScalaCL Compiler Plugin」

 勉強会はBlogを書くところまでが勉強会です。ということで久しぶりに書きます。あいかわらずのLTの時間破り魔っぷりでした。申し訳ありませんでした。15分ぐらいのMiddle Talk的な枠が欲しいです。

 LT内容は「ScalaからGPUを使ってみるScalaCL Plugin」、ScalaコードをOpenCL C言語に変換してくれるScalaCLというコンパイラプラグインの話をGPUってすごいんだよって話とともにお届けしました。

※ ScalaCLは本当に不安定です。遊びならいいですが、これでクリティカルな計算をしようとは思わないでください。

Scala+ScalaCLのベンチマークコード(github)

Scala+JavaCLのサンプルコード(github)

Scala+OpenGL+GLSLのMikuMikuDance(github)

 スライドにも書きましたが、最近のグラフィックボードってすごいですね。Radeon HD 6990を7枚で昔の速い速いと言われていたスーパーコンピュータ「地球シミュレータ(旧型)」と単精度・倍精度とか実効値・理論値とかメモリ転送帯域とかの違いはありますが近い計算性能を出せるんです。これで大気シミュレーションとかできちゃうわけですよ(たぶん)。まあ、10年もたっているわけですから当たり前なのかもしれません。

 GPUは多数のユニット(コア)あるわけですが、それぞれのユニットで別々のプログラムを動かせるというわけではありません。同じプログラムで一度に並列にデータを処理するのが得意です。その辺がCPUと違います。詳しくは、タスク並列とデータ並列で調べてみてください。GPUが得意な方のデータ並列は、Scalaコレクションのmap/filterメソッドと非常に親和性が高いです。ScalaCLが出てきた背景にもそんなところがあるんだと思います。

 また、OpenCLの情報ですが、下の「OpenCL入門 - マルチコアCPU・GPUのための並列プログラミング -」が非常に読みやすく、背景、必要な知識が書かれていると思うので、OpenCL/GPGPUに興味のある方はぜひ買ってみてください。ただ、本当の性能を求めたい人は各GPUのベンダが出している細かい最適化方法を調べる必要があります。

 ScalaCLは、Scalaが使っている人が気軽にGPUを使ってみようかと興味を持つという点では非常に良いと思います。ただ、GPGPUの世界は奥が深いので本当の最適化をしようと思ったら、OpenCL C言語は避けては通れません。ScalaCLでは本来のGPUの性能を20%も引き出せないんじゃないでしょうか(ベクトル演算や特殊な関数が使えないという意味でも)。また、本格的にOpenCLを使ってみようという人にはJavaCLは適度に抽象化されていておすすめです。上にScalaでのサンプルコードをおいておきました。今後も適宜追加していこうと思います。あと、簡単に使うという意味ではRubyのBarracudaも非常に簡単にOpenCL Cを呼び出せそうです。

 株式会社ユーザベースでは一緒に働いてくれる人を募集中です。OpenCL全然関係ないですが。JavaとかScalaとかWicketとかMongoDBとか興味のある方はご連絡ください。

2011-05-06

S2FrameworkのScalaのアクセッサ・ミューテータへの対応方法の検討

 S2Frameworkは、JavaBeansのプロパティの規約に基づいて、getter/setterもしくはpublicのフィールドをオブジェクトのプロパティとして認識するようになっています。ScalaでS2Frameworkを利用する場合、この規約に適合させるためvarフィールドに@BeanPropertyアノテーションをつけるのですが、S2Daoのエンティティなんかを定義しようとすると@BeanPropertyだらけになってしまって見苦しいので、S2Framework側で何とかならないか調べてみました。

 ひとまず、ミューテータのメソッド名の最後に"_$eq"がつくことに注目して、これがついているメソッドを発見したら、これと組になっているアクセッサを探してきてプロパティとして認識させるようにしてみました。変更する点はBeanDescImplクラスのsetupPropertyDescs()です。

Index: src/main/java/org/seasar/framework/beans/impl/BeanDescImpl.java
===================================================================
--- src/main/java/org/seasar/framework/beans/impl/BeanDescImpl.java (revision 4494)
+++ src/main/java/org/seasar/framework/beans/impl/BeanDescImpl.java (working copy)
@@ -558,6 +558,16 @@
                 String propertyName = decapitalizePropertyName(methodName
                         .substring(3));
                 setupWriteMethod(m, propertyName);
+            } else if (methodName.endsWith("_$eq")) {
+                String propertyName = methodName.substring(0, methodName
+                        .length() - 4);
+                setupWriteMethod(m, propertyName);
+                try {
+                    Method readMethod = beanClass.getMethod(propertyName,
+                            new Class[0]);
+                    setupReadMethod(readMethod, propertyName);
+                } catch (NoSuchMethodException e) {
+                }
             }
         }
         for (Iterator i = invalidPropertyNames.iterator(); i.hasNext();) {

これで

@Bean(table = "config")
class Config extends Serializable {
  @Id
  var key: String = _
  var value: String = _
}

みたいにS2Daoのエンティティを書いていけば使えるようになります。

 ただ、このままだとvalフィールドなどのアクセッサのみが定義されたプロパティを認識させることができないのでどうしたものかと考えています。S2Daoで使う分にはこれだけでも十分なんですけどね。

 他に問題が発生しないかどうかもいろいろ使ってみつつ探します。

2011-04-16

JerichoとJaxenでHTMLスクレイピング in Scala

 Scala勉強会第29回 in 秋葉原で、WebスクレイパーつくるのにJavaだとJerichoJaxenつかうとXPathで書けて何かと楽だという話をしたのですが、Scalaで使ったこと無かったのでどんなものかと思ってちょこっと書いてみました。

 Jerichoはbadly formattedなHTMLでもかまわず解析してしまうパーサです。フォーマットとか余計なホワイトスペースの削除とかもできるみたいですが、その辺はおまけ機能ということで。Jaxenの方はツリー構造を持っているものならかまわずXPathで要素を取得することのできるライブラリです。ちょっと違うかもしれませんが、だいたいあってると思います。この二つは特に関係のないライブラリなので、こちらのXPath over HTML using Jericho and Jaxenを参考に連携させるためのコードを書きます。あまり中身を解析する気が起きないのでほぼコピペです。で、

こんな感じで書くと

The Scala IDE for Eclipse beta 2 available now!
  /node/9222
Two Day Scala Workshop in Boulder, Colorado
  /node/9083
Scalathon 2011
  /node/9152
Scala Days - Shaping Up!
  /node/9129
Scala IDE for Eclipse - New Version
  /node/8990
Scala 2.9.0 RC1
  /node/8976

こんな感じの出力が得られます。

 特にこれと言っておもしろいことはしていませんが、JerichoのSegmentクラス(たぶんDOMでいうノード+属性の基底クラス)にimplicit convertionを使ってXPathを評価するeval()というメソッドを追加してみています。

 全ソースコードはこちらから。mavenでビルドできるようになっています。

$ mvn scala:run -DmainClass=st.chimera.scraper.Main

で起動できます。

2011-03-24

Scalaの並列コレクションで実際に並列化されているメソッドを調べてみた

追記 (2011/05/13)

 2.9.0が出たので再実行しました。

 2.9.0から入るScalaの並列コレクションで実際に並列処理されるメソッドについて調べてみました。scaladocに記述されているなどどこかにドキュメント化されていれば良かったのですが、ちょっと見当たらなかったのでparallelパッケージのクラスファイルをJavassistつかって解析して調べてました。並列化されているかどうかの判別は、

  1. scala.collection.parallel.Tasks.executeAndWaitResult()を呼び出しているメソッド
  2. 1.を呼び出しているメソッド

で行っています。ただし、invokevirtualやinvokeinterfaceの解析時にクラスツリーをたどっていないので、スーパークラス、サブクラスの名前で呼び出されていたりすると、メソッドが抜けてしまうと思います。(誰か直してくださるとうれしい ^^;)また、場合によってはサブクラスでオーバーライドされシングルスレッドになってしまっているメソッドもあるかもしれません。

対象ビルド

  • Scala version 2.9.0.r24623-b20110329030130

抽出したメソッドの一覧

scala.collection.parallel.ParIterableLike#++
scala.collection.parallel.ParIterableLike#aggregate
scala.collection.parallel.ParIterableLike#copyToArray
scala.collection.parallel.ParIterableLike#count
scala.collection.parallel.ParIterableLike#drop
scala.collection.parallel.ParIterableLike#dropWhile
scala.collection.parallel.ParIterableLike#exists
scala.collection.parallel.ParIterableLike#filter
scala.collection.parallel.ParIterableLike#filterNot
scala.collection.parallel.ParIterableLike#find
scala.collection.parallel.ParIterableLike#fold
scala.collection.parallel.ParIterableLike#forall
scala.collection.parallel.ParIterableLike#foreach
scala.collection.parallel.ParIterableLike#groupBy
scala.collection.parallel.ParIterableLike#max
scala.collection.parallel.ParIterableLike#maxBy
scala.collection.parallel.ParIterableLike#min
scala.collection.parallel.ParIterableLike#minBy
scala.collection.parallel.ParIterableLike#partition
scala.collection.parallel.ParIterableLike#product
scala.collection.parallel.ParIterableLike#reduce
scala.collection.parallel.ParIterableLike#reduceOption
scala.collection.parallel.ParIterableLike#scan
scala.collection.parallel.ParIterableLike#slice
scala.collection.parallel.ParIterableLike#span
scala.collection.parallel.ParIterableLike#splitAt
scala.collection.parallel.ParIterableLike#sum
scala.collection.parallel.ParIterableLike#take
scala.collection.parallel.ParIterableLike#takeWhile
scala.collection.parallel.ParIterableLike#toArray
scala.collection.parallel.ParIterableLike#toMap
scala.collection.parallel.ParIterableLike#toParCollection
scala.collection.parallel.ParIterableLike#toParMap
scala.collection.parallel.ParIterableLike#toSeq
scala.collection.parallel.ParIterableLike#toSet
scala.collection.parallel.ParIterableLike#zip
scala.collection.parallel.ParIterableLike#zipAll
scala.collection.parallel.ParIterableLike#zipWithIndex
scala.collection.parallel.ParIterableViewLike#partition
scala.collection.parallel.ParIterableViewLike#span
scala.collection.parallel.ParSeqLike#+:
scala.collection.parallel.ParSeqLike#:+
scala.collection.parallel.ParSeqLike#indexWhere
scala.collection.parallel.ParSeqLike#lastIndexWhere
scala.collection.parallel.ParSeqLike#padTo
scala.collection.parallel.ParSeqLike#patch
scala.collection.parallel.ParSeqLike#reverse
scala.collection.parallel.ParSeqLike#segmentLength
scala.collection.parallel.ParSeqLike#zip
scala.collection.parallel.ParSeqViewLike#groupBy
scala.collection.parallel.SeqSplitter#reverse
scala.collection.parallel.immutable.HashMapCombiner#groupByKey
scala.collection.parallel.immutable.HashMapCombiner#result
scala.collection.parallel.immutable.HashSetCombiner#result
scala.collection.parallel.immutable.ParHashMap#drop
scala.collection.parallel.immutable.ParHashMap#dropWhile
scala.collection.parallel.immutable.ParHashMap#filter
scala.collection.parallel.immutable.ParHashMap#filterNot
scala.collection.parallel.immutable.ParHashMap#groupBy
scala.collection.parallel.immutable.ParHashMap#slice
scala.collection.parallel.immutable.ParHashMap#take
scala.collection.parallel.immutable.ParHashMap#takeWhile
scala.collection.parallel.immutable.ParHashMap#toSeq
scala.collection.parallel.immutable.ParHashMap#toSet
scala.collection.parallel.immutable.ParHashSet#drop
scala.collection.parallel.immutable.ParHashSet#dropWhile
scala.collection.parallel.immutable.ParHashSet#filter
scala.collection.parallel.immutable.ParHashSet#filterNot
scala.collection.parallel.immutable.ParHashSet#groupBy
scala.collection.parallel.immutable.ParHashSet#slice
scala.collection.parallel.immutable.ParHashSet#take
scala.collection.parallel.immutable.ParHashSet#takeWhile
scala.collection.parallel.immutable.ParHashSet#toMap
scala.collection.parallel.immutable.ParHashSet#toSeq
scala.collection.parallel.immutable.ParIterable#toSeq
scala.collection.parallel.immutable.ParRange#drop
scala.collection.parallel.immutable.ParRange#dropWhile
scala.collection.parallel.immutable.ParRange#filter
scala.collection.parallel.immutable.ParRange#filterNot
scala.collection.parallel.immutable.ParRange#groupBy
scala.collection.parallel.immutable.ParRange#reverse
scala.collection.parallel.immutable.ParRange#slice
scala.collection.parallel.immutable.ParRange#take
scala.collection.parallel.immutable.ParRange#takeWhile
scala.collection.parallel.immutable.ParRange#toMap
scala.collection.parallel.immutable.ParRange#toSet
scala.collection.parallel.immutable.ParVector#drop
scala.collection.parallel.immutable.ParVector#dropWhile
scala.collection.parallel.immutable.ParVector#filter
scala.collection.parallel.immutable.ParVector#filterNot
scala.collection.parallel.immutable.ParVector#groupBy
scala.collection.parallel.immutable.ParVector#reverse
scala.collection.parallel.immutable.ParVector#slice
scala.collection.parallel.immutable.ParVector#take
scala.collection.parallel.immutable.ParVector#takeWhile
scala.collection.parallel.immutable.ParVector#toMap
scala.collection.parallel.immutable.ParVector#toSet
scala.collection.parallel.mutable.ParArray#drop
scala.collection.parallel.mutable.ParArray#dropWhile
scala.collection.parallel.mutable.ParArray#filter
scala.collection.parallel.mutable.ParArray#filterNot
scala.collection.parallel.mutable.ParArray#groupBy
scala.collection.parallel.mutable.ParArray#map
scala.collection.parallel.mutable.ParArray#reverse
scala.collection.parallel.mutable.ParArray#scan
scala.collection.parallel.mutable.ParArray#slice
scala.collection.parallel.mutable.ParArray#take
scala.collection.parallel.mutable.ParArray#takeWhile
scala.collection.parallel.mutable.ParArray#toMap
scala.collection.parallel.mutable.ParArray#toSet
scala.collection.parallel.mutable.ParHashMap#drop
scala.collection.parallel.mutable.ParHashMap#dropWhile
scala.collection.parallel.mutable.ParHashMap#filter
scala.collection.parallel.mutable.ParHashMap#filterNot
scala.collection.parallel.mutable.ParHashMap#groupBy
scala.collection.parallel.mutable.ParHashMap#slice
scala.collection.parallel.mutable.ParHashMap#take
scala.collection.parallel.mutable.ParHashMap#takeWhile
scala.collection.parallel.mutable.ParHashMap#toMap
scala.collection.parallel.mutable.ParHashMap#toSeq
scala.collection.parallel.mutable.ParHashMap#toSet
scala.collection.parallel.mutable.ParHashMapCombiner#result
scala.collection.parallel.mutable.ParHashSet#drop
scala.collection.parallel.mutable.ParHashSet#dropWhile
scala.collection.parallel.mutable.ParHashSet#filter
scala.collection.parallel.mutable.ParHashSet#filterNot
scala.collection.parallel.mutable.ParHashSet#groupBy
scala.collection.parallel.mutable.ParHashSet#slice
scala.collection.parallel.mutable.ParHashSet#take
scala.collection.parallel.mutable.ParHashSet#takeWhile
scala.collection.parallel.mutable.ParHashSet#toMap
scala.collection.parallel.mutable.ParHashSet#toSeq
scala.collection.parallel.mutable.ParHashSet#toSet
scala.collection.parallel.mutable.ParHashSetCombiner#result
scala.collection.parallel.mutable.ParIterable#toSeq
scala.collection.parallel.mutable.ResizableParArrayCombiner#allocateAndCopy
scala.collection.parallel.mutable.UnrolledParArrayCombiner#result

抽出プログラム

2011-03-20

ScalaからJRubyを利用してみる

 IKボーンの処理がうまくいかないので、ちょっと頭を冷やすために別の部分の調べ物です。IKボーンの処理はAS3版の処理を真似てしまうべきなのですかねぇ。

 さて、ゲーム作りというかゲームエンジンの作りとしてよくあることなのですが、スピードの必要無い部分をスクリプト言語で書けるようにすると、細々とした調整を加えてもプログラムそのものをコンパイル&再起動する必要無くさくさく開発することができます。

 ええ、Scalaのコンパイルは激遅いですから。

 C++での開発だとスクリプト言語にLuaという言語ががよく採用されるようですが、せっかくJavaにはJRubyという強力すぎるスクリプト言語があるのでこれを使います。今回は、Rubyで定義されたクラスのオブジェクトをScala側に返しそのメソッドをインターフェース越しに呼び出すということを試してみました。

 まずは、sbtの設定です。jrubyの本体はmavenレポジトリに上がっているので、それを使います。1.6.0は出たばっかりですね。加えて、sbtのクラスローダを使って起動するとJRubyのクラスローダが残ってしまうのか数回起動するだけで"OutOfMemoryError: PermGen space"で落ちてしまうのでforkして起動するようにします。

import sbt._

class ScalaJRubyProject(info: ProjectInfo) extends DefaultProject(info) {
  val jruby = "org.jruby" % "jruby" % "1.6.0"

  override def fork = forkRun
}

 次にプログラム本体です。JavaからJRubyを呼び出す方法としてJSR223とかいくつかあるのですが、特化されたインターフェースが多数用意されているので、JRubyしか使わないのであればJRuby同梱のRedBridgeと呼ばれるAPIを使うのがよさそうです。下のコードだとorg.jruby.embed.ScriptingContinerになります。細かいパラメータの使用法は調べ切れていませんが、ひとまず呼び出すだけであれば下記の通りで良いようです。

 あとは、適当なインターフェースを実装したクラスをRubyで書いて、そのオブジェクトを生成して返すようにしてやります。このスクリプトを、runScriptlet()で評価してやるとScala側にIRubyObjectが返るので先ほどのインターフェースにキャストしてメソッドを呼び出すとみごとにRuby側のコードが呼ばれます。JRubyいけてますね。そのままキャストして呼び出せると思っていませんでした。Scalaの実装のないtraitでも問題ないですね。

package org.karatachi.scala

import scala.collection.JavaConversions._
import java.io._
import org.jruby.embed._

object Bootstrap {
  val script = """
require 'java'
class Test
  include org.karatachi.scala.IExecutable
  def exec
    puts "Hello World"
  end
end

Test.new
  """

  def main(args: Array[String]) {
    val container = new ScriptingContainer
    val scene = container.runScriptlet(script).asInstanceOf[IExecutable]
    scene.exec
  }
}

trait IExecutable {
  def exec(): Unit
}

 実行すると数秒待たされた後"Hello World"と出力されます。数秒待たされるのはRubyのランタイムを立ち上げてるからと思われますが、Scalaのコンパイル時間に比べたらかわいいものです。