[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[jfriends-ml 10096] Re: Java 言語で学 ぶデザインパターン入門第 6 回議事録
こんばんは。武川です。
ずいぶん長くなってしまいました。。。
From: Murayama Toshikiyo <murayama@xxxxxxxxxxxxx>
Subject: [jfriends-ml 10095] Re: Java 言語で学ぶデザインパターン入門第 6 回議事録
Date: Sun, 05 May 2002 17:40:42 +0900
> (株)ネットジーンの村山です.
>
> 断言はできませんが,
> > > StringBuffer b = new StringBuffer();
> > > synchronized (b)
> > > {
> > > b.append(foo);
> > > b.append(bar);
> > > b.append(baz);
> > > }
> これって早くなりますかね?
>
> モニタの取得/開放が一回ずつ増える分だけ,かえって
> 遅くなりそうな気がしますが.ロジックから見ても,よほど
> 器用な(或いはひねくれた)最適化をしない限り,まず早く
> なることはないと思います.
そう言われるまでなんとなくでしか考えていなかったので、
プログラムを書いて試してみました。
> > > synchronizedの実装にもよるだろうけど、確かに速そうです。
> 実装者の考え方としては,
> 1,モニタを二回以上取得するのは稀だから,二回以上の時が
> 若干遅くなっても一回だけの時に最適化してモニタを実装する.
> 2,二回以上の時も一回だけの時も,同じになるように実装する.
>
> のどちらかでしょう.2の場合はまだいいんですが,
> 1の場合は明らかに遅くなりますね.
3.既にロックを取得している場合は、ロック獲得処理を行なわない。
という処理の仕方はないのでしょうか?
#僕はそういう処理の仕方もあるかなと思ったので速くなるのかと
#おもってました。
で、以下のプログラムを作って時間を計ってみたところ、
「差はない」
というのが結果でした。
まず環境を載せておきます。
Windows2000Server(SP2)
CPU Pentium IV 1.6GHz
Memory 1024MB
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
次にプログラムを載せます。
--------------ここから--------------
/** sychronizedしてからappendを呼ぶ */
public class LockTestOuter {
static int loop= 1024;
static int max = 1024 * 1024 + 1;
public static void main(String args[]){
while(loop < max) {
long start = System.currentTimeMillis();
outSynchronized();
long time = System.currentTimeMillis() - start;
System.out.println(loop+" "+ time);
loop *= 2;
}
}
public static void outSynchronized(){
StringBuffer buf = new StringBuffer();
synchronized(buf){
for(int i= 0;i< loop ;i++){
buf.append("aaa");
}
}
}
}
--------------ここまで--------------
--------------ここから--------------
/** sychronizedせずにappendを呼ぶ */
public class LockTestInner {
static int loop= 1024 ;
static int max = 1024 * 1024 + 1;
public static void main(String args[]){
while(loop < max ) {
long start = System.currentTimeMillis();
normal();
long time = System.currentTimeMillis() - start;
System.out.println(loop+" "+ time);
loop *= 2;
}
}
public static void normal(){
StringBuffer buf = new StringBuffer();
for(int i= 0;i< loop ;i++){
buf.append("aaa");
}
}
}
--------------ここまで--------------
ループをして何度もメソッドを呼んでいるのは、
サイズで変化するのか見たかったのと、HotSpot
は何回かメソッドを呼ばないと最適化しないという
ような記憶(怪しげ)があったからです。
そして結果ですが、プログラムを何度か実行して
一番短かい時間で終わったものを載せています。
E:\project\java>java LockTestOuter
java LockTestOuter
1024 0
2048 0
4096 0
8192 10
16384 0
32768 20
65536 40
131072 70
262144 130
524288 221
1048576 410
E:\project\java>java LockTestInner
java LockTestInner
1024 10
2048 0
4096 0
8192 0
16384 10
32768 11
65536 40
131072 80
262144 120
524288 230
1048576 411
10msのずれが途中で入っているのはGCの影響と考えられます。
結果を見た限りでは、ほとんど差がありません。
というわけで、他のスレッドと競合しない場合は、
synchronizedを外側で宣言しても意味がないということに
なります。
あと、JDK1.1.8_009 で試した結果も載せておきます。
E:\project\java>java LockTestOuter
java LockTestInner
1024 0
2048 0
4096 0
8192 10
16384 0
32768 10
65536 30
131072 40
262144 90
524288 171
java.lang.OutOfMemoryError
at java.lang.StringBuffer.append(Compiled Code)
at LockTestInner.normal(Compiled Code)
at LockTestInner.main(Compiled Code)
E:\project\java>java LockTestOuter
java LockTestOuter
1024 0
2048 0
4096 0
8192 10
16384 0
32768 10
65536 20
131072 51
262144 90
524288 170
java.lang.OutOfMemoryError
at java.lang.StringBuffer.append(Compiled Code)
at LockTestOuter.outSynchronized(Compiled Code)
at LockTestOuter.main(Compiled Code)
サイズが大きいとErrorになりますが、実行速度自体は
JDK1.1.8の方が速いようです。不思議ですね。
今後の調べてみたいこととしては、
・JDK1.0.2のような昔のバージョンでも同じなのか?
・synchronizedがないappendは本当に速いのか?
がありますが、気がむけばしらべてみたいと思います。
なんかおかしい点など合ったら指摘して頂けると嬉しいです。
最後に、こういう面白い機会を作ってくれた村山さんと、高橋
さんにに感謝します。
ではでは。