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+OpenGL+GLSLのMikuMikuDance(github)
スライドにも書きましたが、最近のグラフィックボードってすごいですね。Radeon HD 6990を7枚で昔の速い速いと言われていたスーパーコンピュータ「地球シミュレータ(旧型)」と単精度・倍精度とか実効値・理論値とかメモリ転送帯域とかの違いはありますが近い計算性能を出せるんです。これで大気シミュレーションとかできちゃうわけですよ(たぶん)。まあ、10年もたっているわけですから当たり前なのかもしれません。
GPUは多数のユニット(コア)あるわけですが、それぞれのユニットで別々のプログラムを動かせるというわけではありません。同じプログラムで一度に並列にデータを処理するのが得意です。その辺がCPUと違います。詳しくは、タスク並列とデータ並列で調べてみてください。GPUが得意な方のデータ並列は、Scalaコレクションのmap/filterメソッドと非常に親和性が高いです。ScalaCLが出てきた背景にもそんなところがあるんだと思います。
また、OpenCLの情報ですが、下の「OpenCL入門 - マルチコアCPU・GPUのための並列プログラミング -」が非常に読みやすく、背景、必要な知識が書かれていると思うので、OpenCL/GPGPUに興味のある方はぜひ買ってみてください。ただ、本当の性能を求めたい人は各GPUのベンダが出している細かい最適化方法を調べる必要があります。
OpenCL入門 - マルチコアCPU・GPUのための並列プログラミング -
- 作者: 株式会社フィックスターズ,土山了士,中村孝史,飯塚拓郎,浅原明広,三木聡
- 出版社/メーカー: インプレスジャパン
- 発売日: 2010/01/22
- メディア: 単行本
- 購入: 4人 クリック: 113回
- この商品を含むブログ (18件) を見る
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だとJerichoとJaxenつかうと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つかって解析して調べてました。並列化されているかどうかの判別は、
- scala.collection.parallel.Tasks.executeAndWaitResult()を呼び出しているメソッド
- 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のコンパイル時間に比べたらかわいいものです。

