■掲示板に戻る■ ■過去ログ倉庫めにゅーに戻る■
Javaの速度あげたい
1 名前: 名無しさん@お腹いっぱい。 投稿日: 2000/08/09(水) 08:06
遅いので。


2 名前: 名無しさん@お腹いっぱい。 投稿日: 2000/08/09(水) 08:06
import java.awt.*;
import java.awt.event.*;

public class LifeGame extends Frame{

final int N = 100;
final int M = 100;
int a[][] = new int[N+2][M+2],b[][] = new int[N+2][M+2];
int i, j;

Image offs;
Graphics grf;

public void update(Graphics g){
paint(g);
}

public void paint(Graphics g){

for(;;){
grf.setColor(Color.black);
grf.fillRect(0,0,1050,1050);
grf.setColor(Color.green);
for(i=1;i<N+1;i++){
for(j=1;j<M+1;j++){
if(a[i][j]==1){
grf.fillOval(j*10+10,i*10+30,10,10);
b[i-1][j-1]++; b[i-1][j]++; b[i-1][j+1]++;
b[i ][j-1]++; b[i ][j+1]++;
b[i+1][j-1]++; b[i+1][j]++; b[i+1][j+1]++;
}else
grf.drawOval(j*10+10,i*10+30,10,10);
}
}
g.drawImage(offs,0,0,this);

for(i=0;i<=N+1;i++){
for(j=0;j<=M+1;j++){
if(b[i][j]!=2)
a[i][j]=(b[i][j]==3?1:0);
b[i][j]=0;
}
}
}

}

public LifeGame(){
addNotify();
offs = createImage(1050,1050);
grf = offs.getGraphics();

for(i=0;i<N;i++)
for(j=0;j<M;j++)
a[i][j]=0;

for(i=0;i<N;i++)
for(j=0;j<M;j++)
a[i][j]=((int)(Math.random()*1000)>500?1:0);
}

public static void main(String args[]){
Frame frame = new LifeGame();
frame.setSize(1050,1050);
frame.show();
}

}



3 名前: 名無しさん@お腹いっぱい。 投稿日: 2000/08/09(水) 09:06
どうでもいいが、Java板になりつつあるな。


4 名前: Aランチ 投稿日: 2000/08/09(水) 17:01
一世代前のbを見ればa.は計算できるわけだ。
だからpaint()内の後ろの2重ループは必要なさそうだ。
前のループでいっぺんに処理できる。クリッピングが必要になるが。


5 名前: 名無しさん@お腹いっぱい。 投稿日: 2000/08/10(木) 03:06
fillOvalをfillRectで置換


6 名前: momus 投稿日: 2000/08/10(木) 09:45
アルゴリズム面での高速化を望んでいるようではなかったので、
paintのとこだけコードの最適化(とまで行かない)してみました。

パフォーマンスチェックはしてないので、
実際に速くなったかは分かりません。
(見た目ではあまりかわらなかったが。。。)

C言語みたいにポインターが使えたら楽なのに、と、
今更ながら実感しましたね。

public void paint(Graphics g){

for(;;){
grf.setColor(Color.black);
grf.fillRect(0,0,1050,1050);
grf.setColor(Color.green);
int Np = N + 1;
int Mp = M + 1;
for(i=1;i<Np;i++){
int i10_30 = i * 10 + 30;
int bi[] = b[i];
int bip[] = b[i+1];
int bim[] = b[i-1];
for(j=1;j<Mp;j++){
int j10_10 = j * 10 + 10;
int jp = j + 1;
int jm = j - 1;
if(a[i][j]==1){
grf.fillOval(j10_10,i10_30,10,10);
bim[jm]++; bim[j]++; bim[jp]++;
bi[jm]++; bi[jp]++;
bip[jm]++; bip[j]++; bip[jp]++;
}else
grf.drawOval(j10_10,i10_30,10,10);
}
}
g.drawImage(offs,0,0,this);

for(i=0;i<=Np;i++){
int ai[] = a[i];
int bi[] = b[i];
for(j=0;j<=Mp;j++){
if(bi[j]!=2)
ai[j]=(bi[j]==3?1:0);
bi[j]=0;
}
}
}

}



7 名前: 投稿日: 2000/08/10(木) 13:12
>4
クリッピングってのはjava.awt.Graphics2Dにあるclipなんすかね?
なんだろこれ・・・?
>5
OvalをRectにしたら、ものすごく速くなりました。
円の処理は遅いってことを実感しました。
(確に考えてみれば円は四角にくらべると遅そうだ)
>6
う〜ん、??な感じです。ポインタ使うとどんなコードに
なるんだろ?


8 名前: 5 投稿日: 2000/08/10(木) 20:56
>7
 スマン。寝る直前だったので片手オチな解答でした。draw も fill も、Oval ->
Rect だった。

 円はどうしても計算しながら描かないといけないので(一部のアクセラレーショ
ン付きを除く)数が大量なだけにどうしても遅くなってしまう。今回のはここら辺
が一番ネックになってたみたい。
 で、この場合あらかじめ円を描いた Image オブジェクトを作成しておいて
drawImage で張り付けてやるだけで、かなりの速度が稼げるはず。
 せっかく画面と同じ大きさのバッファを持ってるのに素直に毎回描写しているの
は無駄。>4のクリッピングはこのあたりの指摘かな?
 イベント処理で永久ループしてしまうのは、VM の実装によってはやばいことにな
りそう。

>6
 高速化はアルゴリズムで決まると思うがどうだろ? そのようなことは、よりよ
いアルゴリズムが思い浮かばないときや、時間がないとき(よくありそう)にどうしてももっと早くしたい場合にしか俺は使わないが。だって読みづらいもん。



9 名前: 趣味グラマ=5 投稿日: 2000/08/10(木) 20:58
 で、実際のプログラム。つっこみよろしく>all

import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class LifeGameS extends Frame{

 final int N = 100;
 final int M = 100;
 int a[][] = new int[N+2][M+2],b[][] = new int[N+2][M+2];

 private int width, height;

 Image offs, live, dead;
 Graphics grf;
 Insets ins;

 protected void drawGenerationShift() {
  // 世代交代
  for(int i=1;i<N+1;i++){
   System.arraycopy(a[i], 0, b[i], 0, M+2);
   for(int j=1;j<M+1;j++){
    int c = a[i-1][j-1] + a[i-1][j] + a[i-1][j+1] +
    a[i ][j-1] + a[i ][j+1] +
    a[i+1][j-1] + a[i+1][j] + a[i+1][j+1];
    if ( c!=2 ) {
     int o=b[i][j], p;
     b[i][j] = p = (c==3 ? 1 : 0);
     if ( o != p )
      grf.drawImage((p==1?live:dead),j*10+ins.left,i*10+ins.top,this);
      // 生死が変わった場合のみ描写
    }
   }
  }

  int[][] c = b; b = a; a = c; // 配列の交換
 }

 protected void drawGeneration() {
  // 世代全描写
  grf.clearRect(0,0,width,height);
  for(int i=1;i<N+1;i++)
   for(int j=1;j<M+1;j++)
   grf.drawImage((a[i][j]==1?live:dead),j*10+ins.left,i*10+ins.top,this);
 }

 public void update(Graphics g){
  g.drawImage(offs,0,0,this);
  drawGenerationShift();
  repaint();
 }

 public void paint(Graphics g){
 // g.drawImage(offs,0,0,this);
  repaint();
 }

 public LifeGameS(){
  setBackground(Color.black);
  addNotify();
  ins = getInsets();
  width = M * 10 + 20 + ins.left + ins.right;
  height = N * 10 + 20 + ins.top + ins.bottom;
  setSize(width,height);

  offs = createImage(width,height);
  grf = offs.getGraphics();

  live = createImage(10,10);
  Graphics grf1 = live.getGraphics();
  grf1.setColor(Color.green);
  grf1.fillOval(0,0,10,10);

  dead = createImage(10,10);
  grf1 = dead.getGraphics();
  grf1.setColor(Color.green.darker()); // 暗い緑
  grf1.drawOval(0,0,10,10);

  for(int i=1;i<N+1;i++)
   for(int j=1;j<M+1;j++)
    a[i][j]=(Math.random()>.5?1:0);

  drawGeneration();
 }

 public static void main(String args[]){
  Frame frame = new LifeGameS();
  frame.show();
 }

}



10 名前: 名無しさん@お腹いっぱい。 投稿日: 2000/08/10(木) 22:47
速度的なことを考えるのなら paint() のところであまり色んな処理をしたがらない方が良いと教わったんですけど



11 名前: 4 投稿日: 2000/08/10(木) 22:50
>5
そういう事は全然考えてませんでした。
言いたかったのは、フィールドの周りのことね。
9は2と比べるとフィールドが一回り小さくなってる。
しかし9は速いですね。


12 名前: 5 投稿日: 2000/08/11(金) 07:15
>>4
 ウィンドウが一回り小さくなってる事ですが、元のプログラムでは、 Frame のサ
イズが固定でした。が、こっちの環境だとウィンドウのタイトルバーに一部隠れそ
うだったので、Insets を求めてから、マージンをとって Frame のサイズを決定し
てる。Java は多くの環境で動作するので、(自分だけが使うのでなければ)こうい
う処理は常に必要だと思う。>>1

>>10
 >>2 への指摘かな? paint() は、ウィンドウの重なりが変わったときに(clip
付きで)連続して呼ばれることがあるので、必要最低限の処理に >>9 はしてある。
まあ、こんなものではないでしょうか? update() でもあんまり良くはないんだが.
..
 というわけで、>>9 はあんまりいいプログラムではなかったので、厳しいつっこ
みはやめてくれ >all



13 名前: momus=6 投稿日: 2000/08/11(金) 13:18
>8
>高速化はアルゴリズムで決まると思うがどうだろ?

もちろん、
アルゴリズムを見直す方が格段に速くなる可能性がありますよね。
個人的にもコーディングレベルの高速化よりも、
アルゴリズムで悩むほうが好きだし。

好みで言うと、
ライフゲームとかは、毎フレーム描画する必要ないんじゃないかと思う。
スレッド作って一定間隔で計算(世代交代)して、
それよりも大雑把な周期で描画すると。
とにかく描画が遅いから。



14 名前: 10 投稿日: 2000/08/11(金) 15:17
>>12
すいません。書き方が悪くて。
おっしゃるとおり>>2,(6もかな?)へのレスでした。
そう言う意味で>>9のはしっくり来るけど、そうかupdate()でもあまり良くないんですか…

>>13
そのやり方は激しく同意
計算・描画では今のところ遅過ぎて頭に来てしまうので…

LifeGameなら
http://www.mindspring.com/~alanh/life/index.html
の奴なんかは良く出来ていると思いますね。流石!


15 名前: 名無しさん@お腹いっぱい。 投稿日: 2000/08/11(金) 18:28
オフスクリーンを使わない。




16 名前: 投稿日: 2000/08/11(金) 19:32
>こっちの環境だとウィンドウのタイトルバーに一部隠れそうだったので
僕はUNIXです。UNIXだとタイトルバーに隠れそうだったのでyをi*10+30
とかってやってました(一応の処理)。Insetsというのを知らなかったので
そんなしょぼいことで対処してたつもりでした。
あとpaint()の動作ってのが全然わかってないかも。ウインドウの状態が変
わったときにpaint()が呼び出されるってことを実感してるだけ・・・。


17 名前: 5 投稿日: 2000/08/11(金) 19:38
>>13
そういう方法なら同意。
ただひとつ。ライフゲームに関しては、群の世代推移を見るゲームだったと思うので、
世代交代と同じタイミングで描写しないとおもしろくないと思います。
「とか」って書いてあったからよけいなつっこみだったらすいません。

>>14
俺も書き方が悪かったようですみません。
俺が指摘したかったのは「イベント処理でよけいな時間をかけないこと」
paint() は上でも書いたように連続して呼び出されることがあるため、最低限の
処理に押さえた方がいいですが、update() は repaint() と対になって呼ばれる
ようで、そんなに時間をかけなければいいと思います。(この辺の切り分けが難しい)

今回の >>9 は update() そのものの処理は(ある程度世代が進めば)短いですけど、
連続して呼び出してるので、イベント処理ががたついてしまってるようです。
これ以上の処理を追加する気なら、13さんの書いてるように、スレッドを作って
そちらで世代交代を行わせてから、repaint() ですかね。

リンク先の LifeGame は、良くできてますね。


18 名前: 5(連続書き込みなのでsage) 投稿日: 2000/08/11(金) 19:40
>>15
確かにそうなのでやってみました。
世代交代は早くなりました。paint() でオフスクリーンを使うようにしてやって、
まあ、満足できる速度が出ました。
けどここまでやると原型あんましとどめてないですね。こんなのでも良ければ
書き込みするから、必要だったら言ってください。>>7

ふう、2chだからって普段使わないような口調で書いてたのですが、
疲れたので普段に戻してます。5=8 と違う言葉使いですが、同一人物ですよ〜


19 名前: 5(連続書き込みなのでsage) 投稿日: 2000/08/11(金) 20:09
オフラインで書いてる間に新しい書き込みがあったので追加。

>>16
UNIXですか。俺は Win98+JDK1.3 を使っています。
俺も以前 UNIX を使って C + Motif で組んでいて、paint() (じゃないけど)
の度に重たい内容を毎回全部素直に描写してたら、
「ウィンドウの内容を表示したままドラッグする」で上を通過されて、
「ダメ!」と食らったことがあります。

ちょっと話が横にそれてしまいましたが、paint() の内容は最低限の処理に
押さえた方がいいです。理由は>>10>>17に書いたとおりです。
今回のような内容でしたら、渡された Graphics オブジェクトから
getClipBounds() を使用してその領域の描写だけに専念するか、
paint() の連続呼び出しが終わった後に、全部を描写するように
すればいいと思います。



20 名前: 1(実家に帰るのでsage) 投稿日: 2000/08/12(土) 14:09
有益なことばかりで非常にうれしく思ってます。
親が帰ってこいというので帰ります。その間はネットとはおさらばです。
http://www.asahi-net.or.jp/~dp8t-asm/java/tips/index.html
の最適化あたりをプリントアウトして勉強します。では。