ラベル 並列処理 の投稿を表示しています。 すべての投稿を表示
ラベル 並列処理 の投稿を表示しています。 すべての投稿を表示

2012年7月8日日曜日

GroovyのmetaClass.defineでインターフェースメソッドをオーバーロードしたいんだけど

寒いですね。このままだとかき氷屋が倒産してしまいますね。

かき氷屋が倒産する→シロップメーカーが倒産する→サトウキビ売れなくなる→沖縄の人困る

いやですね。

みけです。

メソッドをオーバーロードしたいなぁ


グルービー語をやっている人は常々こういう欲求に駆られてしまい、

いつの間にかメソッドをオーバーロードしていることがあります。

たとえば、Integerを単純に表示するだけでは面白く無いので、

toStringメソッドにClosure<&Stringgt;を引数で取るようにして、

出力を変えてみたいと思ってしまいます。

Javaでやると、こいつは大変で、Integerの持っていた計算性を失って、

拡張するという覚悟を取らなければなりませんが、Groovyでは、

Integerの可算性を損なわずに拡張できます。






並行処理プログラミング


まあ、さっきのはどうでもいいんですよ。

事の本質は並行処理プログラミングの話です。

昨日のCSPモデルをjava.util.concurrentを使って模してみた

プログラムを普通に書いてみました。

ちなみに仕様としては

  • 0~9のランダムの数値をスレッド間で送受信する
  • 0が二回連続して送信されると終了する
  • 送受信が終了すると、これまで送受信された数値の合計が返される

といったものです。




…長いですね。

で、嫌なところは、17行目などにあるnew Callable<Integer> @Override Integer call()

といったあたりですね。

非常にまどろっこしいです。

なので、本当はこう書きたいんです。



ExecutorServiceのメソッドsubmitが引数にクロージャーを取れるように

すれば、なんと7行もコードが短くなるんです。

まあ、Java8になれば、おkなんですがね。



グルービスト


Java8まで待てないということもあるので、

そういう場合のグルービストはこんなことをやるのです。



さて、拡張したExecutorServiceを読んでみるとこうなります。




あれ、値が…


値が戻って来ませんね…

とつぶやいているうちにふもさんからアドバイスもらいました。





なるほど、メソッド名がかぶっているとだめなのか…

あれ、でも、さっきのIntegerの場合だとどうしておkなの?

うーんと悩んでいると、えいやさんからこんなアドバイスもらいました。





なるほど、どうやらインターフェースを拡張する場合に発生しうる現象のようですね。

で、インスタンスを得た後に拡張してしまえば、問題ないというわけですね。


簡単にまとめると…


  • インターフェースを拡張する
  • 実装クラスが割り当てられる
  • 実装クラスによって上書き

でも、なんだか腑に落ちませんね。

どうなんでしょう、この動き…

JIRAのイシューでも漁ってみようかな…

2012年7月7日土曜日

JGGUG G*ワークショップ行ってきた

咳が止まりません。

みけです。

JGGUG G*ワークショップ行ってきました。

ちなみにtogetterとかはこちらにあります。

Rod JohnsonがSpring Sourceを卒業したそうです。

ソース1

ソース2

CSPとは?


今回の目玉はCommunicating Sequential Processes(CSP)です。

Occamなどの言語はCSPの思想から作られているそうです。

ちなみにOcamlではありません。

言語的にはErlangに近いそうです。

なお、日本語の解説ページがあります。

こちらを参照して下さい。


JCSP


CSPモデルをJavaで実装したのが、JCSPです。

jarファイルはMavenおよびGradleから取得可能なようです。

下記を参考にして下さい。

pom.xml



build.gradle




サンプルコード


まあ、僕は文章を書くのが(文卒のくせに)苦手なので、

早速サンプルコードを書いてみました。

(正確にはコピペです。)




で、実行結果がコレ。




ログの出力の仕方からわかるのは、Parallelの通信が

出力→入力で一対一になっているところですね。

ChannelQueueのような溜め込むという

操作を提供はしていないようです。

(まあ、そういうChannelもあるかもしれませんが…)

スレッドの遅い方(Readクラス)の速度に合わせて、

通信が行われているあたりが特徴的だと思います。


Groovy


Groovyで実行する場合は、Parallelクラスが

PARというgparsにあるラッパークラスに置き換わる

ところです。




結論


java.util.concurrentパッケージにあるクラスを

用いて、同様の処理を書くことができると思いますが、

それに比べると結構簡単に並列処理が書けそうです。

ちょっと面白そうなので、もう少しいじってみたいと思います。




2011年12月11日日曜日

恥ずかしくて今さら聞けない、mongodb講座 1

とりあえず、 G* Advent Calendarに向けて目下ネタを作成中です。

というわけで、mongodbをいじることにしました。

なぜは聞かないでください。

たんなる思いつきだけで生きていますので…

インストールとサービスの起動


インストールは結構簡単です。

サービスの起動も簡単です。

Macの人


Mac Portsでインストールしました。オレは…


sudo port install mongodb


だったような…(テキトーなので忘れている

まあ、わからなくても、「Max mongodb インストール」とかでググればなんか出てきます。
そんなものです。

ちなみに、portでインストールした場合、ビルドに結構時間がかかりました。

MBA 11inch Core-i7 で2時間くらいかかります。

あと、portでインストールした場合、自動でサービスを起動してくれるコマンドの紹介とか書いてあります。

Windowsの人


とりあえず、mongodbのホームページからzipをダウンロードして適度なフォルダにインストールしましょう。

インストール完了したら、mongod.exeとかいうのをダブルクリックすれば多分サービスが起動します。

Linuxの人


え、オレが教えなくても(ry
sudo apt-getとかでいいんじゃね(テキトー


とりあえず使う


クライアントを起動


とりあえず、使うには次のコマンドを叩いて、クライアントを起動しましょう。


$ mongodb
MongoDB shell version: 2.0.1
connecting to: test
> 


DBの選択



> use mydb
switched to mydb
> 


とりあえずデータを入れてみよう



> db
mydb
> db.items
mydb.items
> db.items.insert({"type" : "data", "value" : 1})
> assert(db.items.find().length() === 1, "insert 1 record, the number of fetch will become 1")
> 


はいデータ1件入っていますね。

とりあえず今日はここまで。



2011年9月11日日曜日

Gradleでスローテスト問題を解決するAgain

前回、こんな記事書きました。
見事に効果がわからないという結果が出てきて、どうやればいいのか考えていましたが、
さすがGroovyクラスタに素晴らしい先輩がいらっしゃいました。

@bluepapa32 先輩です。
単純に、スリープするコード


    for (int i = 0; i < 100; i++ ) {
        Thread.sleep(10);
    }


を埋め込めばよかったわけですね。

というわけで、こんなテスト生成スクリプトでテストを大量に作ってみることにしました。


CreateTest.groovy

import static groovyx.gpars.GParsPool.*;
def packagePath = 'C:/Users/mike/IDEA_Project/GradleSample/src/test/java/orz/mikeneck/gradle/sample/boxunbox/test'

def head = $/
package orz.mikeneck.gradle.sample.boxunbox.test;
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/$

def body = $/
    public static final int SIZE = 100;
    private List<Integer> intList;
    private List<Long> longList;
    @Test
    public void testInteger()   throws InterruptedException {
        int[] array = new int[SIZE];
        int position = 0;
        for(Integer item : intList) {
            array[position++] = item;
            Thread.sleep(10);
        }
        for (int i : array)
            assertThat(i, is(intList.get(i)));
    }
    @Test
    public void testLong()  throws InterruptedException {
        long[] array = new long[SIZE];
        int position = 0;
        for (Long item : longList) {
            array[position++] = item;
            Thread.sleep(10);
        }
        position = 0;
        for(long item : array)
            assertThat(item, is(longList.get(position++)));
    }
    @Before
    public void setUp() throws Exception {
        Integer[] integers = new Integer[SIZE];
        Long[] longs = new Long[SIZE];
        for(int i = 0; i < SIZE; i++)
            integers[i] = new Integer(i);
        intList = Arrays.asList(integers);
        for (int i = 0; i < SIZE; i++)
            longs[i] = new Long(i + Integer.MAX_VALUE);
        longList = Arrays.asList(longs);
    }
}
/$

def numbers = []
(1..300).each {
    numbers << it
}

withPool {
    numbers.collectParallel { number ->
        def className = "BoxUnboxTest${number}"
        def name = "${className}.java"
        def fileName = "${packagePath}/${name}"
        def define = "public class ${className} {"
        def content = new StringWriter()
        content << head
        content << define
        content << body
        println ' ---- '
        println "now processing : $fileName"
        println ' ---- '
        new File(fileName).write(content.toString(), 'UTF-8')
        assert new File(fileName).exists() == true
    }
}


10msecを100回Sleepするテストメソッドが二つで、ひとつのテストの実行にかかる時間は2sec。
これが300個あるので、必要な時間は600sec = 10min かかるテストです。

実際に一つテストを実施してみました。


2secかかっていますね。
これが300個用意されるので、10minかかることが想定されます。

では、次のGradleスクリプトで実行してみましょう。


build.gradle

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}



なお、マシンの環境は次のような感じです。
OS : Windows 7
CPU : Intel Xeon X3460 (8Core) (64bit)
RAM : 8.00GB




では、実行、というかその間に色々と他のことで負荷がかからないように、
IntelliJ IDEAが起動していて、このブログだけがChrome上で起動しているという状態にしておきます。
で、IntelliJのRunツールで実行するのではなく、ふつうにコマンドから起動します。

さて、オレはその間、『JOJOの奇妙な冒険』を読むこととします。

では、いざ実行…




c:\Users\mike\IdeaProjects\TestingGradle>gradle build
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 15 mins 42.983 secs


15分43秒、マジ遅え。
しかも、最初の数個目までのテストは結構サクサク進んでいたけど、200を越えたあたりからテストの実行速度が落ちてきた感じがする。
こういう時はJVMの再起動などが必要なのかもしれん。

というわけで、さっそく、並列処理を試してみようと思う。

build.gradle

apply plugin: 'java'

repositories {
    mavenCentral()
}

dependencies {
    testCompile 'junit:junit:4.8.2'
}

test {
    maxParallelForks = 6
    forkEvery = 30
}



前回にも書きましたが、
  • maxParallelForksは並列するスレッド数。
  • forkEveryは指定した回数テストを実行するとJVMを再起動して、OutOfMemoryExceptionを回避する仕組みです。

では、実行開始です。


c:\Users\mike\IdeaProjects\TestingGradle>gradle build
:buildSrc:compileJava UP-TO-DATE
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes UP-TO-DATE
:buildSrc:jar UP-TO-DATE
:buildSrc:assemble UP-TO-DATE
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build UP-TO-DATE
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
:check
:build

BUILD SUCCESSFUL

Total time: 2 mins 42.544 secs




えっ、2分Σ(゚д゚lll



めちゃくちゃ早くなっていません?
元々15分43秒かかっていたのがたったの2分43秒。

単純に計算すると

15min43sec / 6(core) = 2min37sec


となるから、妥当な値ですね。

では、テストのサマリーを比較してみましょう。



並列化前



並列化後



これを見るとCPU時間は変わっていません。
つまり、CPU時間をすべて複数コアで実行したために早くすることが出来たという事になります。

というわけで、結論

GradlemaxParallelForksを使うともれなくスローテスト問題が解決できる。



2011年7月21日木曜日

ランダムの妥当性とは?~その2

前回はランダムを発生させるところまでやったのだが、発生するランダムな値が本当にランダムかわからないという問題があることがわかったので、今回は少し検証してみようと思う。

まずは、発生している値の平均値が10であるか検証。
これ基本です。


def getAverage = {map ->
    def size = map.size
    def sum = withPool {
        map.parallel
        .map{it.value}
        .reduce{s, i ->
            s + i
        }
    }
    sum / size as double
}

def testMap = [[key:1,value:11], [key:2,value:9], [key:3,value:10]]
assert 10.0 == getAverage(testMap)


下のassertは平均値を算出したメソッドが妥当かテストしているだけです。
では、おもむろにテストしてみる。


10.times{
    assert 10.0 == getAerage(getRandom(20 * 10))
}


とりあえず、大丈夫なようだ。

つぎは分散を算出する。これも基本です。


def getSigmaSquare = { map, avg ->
    def size = (map.size <= 2)? 2 : map.size
    def sum = withPool{
        map.parallel
        .map{
            def dif = it.value - avg as double
            dif * dif
        }
        .reduce{s, i ->
            s + i
        }
    }
    sum / (size - 1) as double
}

def testMap = [[key:1,value:11], [key:2,value:9], [key:3,value:10]]
assert 1.0 == getSigmaSquare(testMap, 10)


下のassertは分散を算出したメソッドが妥当かテストしているだけです。
では、おもむろに実行してみる。

10.times{num ->
    def rand = getRandom(10 * 20)
    def avg = getAverage(rand)
    def sigs = getSigmaSquare(rand, avg)
    def sigm = Math.sqrt(sigs)
    println sprintf("num : %2d / avg : %5.1f / sigs : %5.1f / sigm : %4.1f", num, avg, sigs, sigm)
}


実行結果


num :  0 / avg :  10.0 / sigs :  11.8 / sigm :  3.4
num :  1 / avg :  10.0 / sigs :  10.5 / sigm :  3.2
num :  2 / avg :  10.0 / sigs :  16.1 / sigm :  4.0
num :  3 / avg :  10.0 / sigs :  10.6 / sigm :  3.3
num :  4 / avg :  10.0 / sigs :  10.3 / sigm :  3.2
num :  5 / avg :  10.0 / sigs :  11.6 / sigm :  3.4
num :  6 / avg :  10.0 / sigs :   9.9 / sigm :  3.1
num :  7 / avg :  10.0 / sigs :  10.8 / sigm :  3.3
num :  8 / avg :  10.0 / sigs :   8.5 / sigm :  2.9
num :  9 / avg :  10.0 / sigs :   5.3 / sigm :  2.3


発生した値はだいたい、10 ± 3.5 程度にあるということになるのだろうか。

2011年7月20日水曜日

ランダムの妥当性とは?~その1

小難しいタイトルですが、ランダムの妥当性ってなんなのか考えてみることにしました。

とりあえず、スタートのスクリプトは@fumokmmさんのエントリー『Groovyで範囲内乱数』です。

では、スクリプトをどうぞ。(コピペ)

IntRange.metaClass.define{
    random{
        int from = delegate.isReverse() ? to : from
        int to = delegate.isReverse() ? from : to
        int size = to - from + 1
        (Math.floor(Math.random() * size) + from) as int
    }
}


これで、乱数発生の装置は作成できたので、おもむろに作成した乱数をListにぶっこむ処理を書いてみる。


range = 1..20
def getRandom = {run ->
    def values = []
    run.times{values << range.random()}
    withPool {
        values.parallel
        .map{[it, 1]}
        .groupBy{it[0]}.getParallel()
        .map{it.value = it.value.size();it}
        .sort{it.key}
        .collection
    }
}
そして、実行してみる。
getRandom(20 * 10)
予想される結果としては、1~20までの数値が10個を中心として発生するはず。 実行結果1
[1=4, 2=7, 3=7, 4=14, 5=12, 6=10, 7=12, 8=12, 9=5, 10=10, 11=10, 12=5, 13=8, 14=11, 15=12, 16=19, 17=6, 18=18, 19=10, 20=8]
実行結果2
[1=11, 2=8, 3=11, 4=5, 5=10, 6=10, 7=11, 8=7, 9=14, 10=12, 11=9, 12=11, 13=10, 14=14, 15=10, 16=10, 17=7, 18=12, 19=8, 20=10]
実行結果3
[1=10, 2=10, 3=10, 4=9, 5=14, 6=15, 7=8, 8=11, 9=10, 10=8, 11=7, 12=9, 13=10, 14=10, 15=11, 16=6, 17=10, 18=5, 19=10, 20=17]

む、一応、ランダムに数字が発生しているような気がするが、これはランダムと言えるのだろうか?