« C4D XPressoでの水面のアニメーションをYouTubeにアップしてみました。 | トップページ | C.O.F.F.E.E.やユーザスクリプトから、C4D標準マテリアルのパラメータを調整する。 »

C.O.F.F.E.E.で重たい処理をした時に、プログレスバーを表示させる。

処理速度が速くないC.O.F.F.E.E.で大量のベクトル値やsqrt()平方根関数を扱うと、実行してから復帰するまでにかなり時間がかかります。

処理中、何も表示されないと「フリーズでもしたのかな?」なんて感じる事もあるよね…

そんな時、プログレスバー
Progress_01 レンダリングしている時に情報表示バーに表示される)
を使ってみよう。

では、スクリプトマネージャとコンソールを用意してください。

今回のネタは、ベクトル処理等をせず、ただC.O.F.F.E.E.でループをさせます。

for(i = 0 ; i < n ; i++);

10回や100回のループは直ぐに終わってしまうので、かなり回数の多いループにします。
ただし、いきなり大きな数を指定すると、復帰するまで時間がかかるかも知れないので、徐々に上げていきます。
だいたい10秒~20秒ぐらいのループです。

C.O.F.F.E.E.で時間を扱う関数は、BaseTimeクラス…ではなくて、
実時間を扱う関数があります。

time() C4Dの起動時間 Standard Functions
GeGetSysTime() PCの時計(実時間) BaseFunctions

time()関数の単位は、ミリ秒(整数)です。1000で1秒。
GeGetSysTime()関数は、SysTimeクラスを返します。年、月、日、時、分、秒が別々に取得できます。

今回は、time()関数を使います。time()関数で取得した数値を最終的に1000で割れば秒数が取得できます。

ループに掛かる時間を測定するには、ループ前の実時間をループ終了時間から引けば算出できます。

/*
ループの時間測定
2009.4.28
*/


var
i;
var tm = time();              //開始時間

for(i = 0 ; i < 100000 ; i++);//カラのループ

println(time() - tm);         //終了時間から開始時間を引く

後は、実行ボタンを押して実行します。

12

が表示されました。12ミリ秒
今はC4D R11Demo/PhotoshopやC4D R9.5も立ち上がっています。ついでにIEも…
ちなみに自機は…

CPU:Pen4/3GHz
RAM:2GB
OS:WinXPproSP3

実行時間は約0.01秒
10秒にするには、1000倍と…

/*
ループの時間測定/秒表示
2009.4.28
*/


var
i;
var tm = time();

for(i = 0 ; i < 100000000 ; i++);//ループ回数を修正。ゼロを3個増やしました

println((time() - tm)/1000.0);   //ループに掛かった時間(ミリ秒)を1000.0で割り秒にします。

12.681000

今度は秒数で表示されています。
12.681秒と…

では、これにプログレスバーを追加します。

StatusSetBar([float]f);//0.0 < f < 1.0

これを追加すると、ループに掛かる時間は一気に跳ね上がります。
だいたい、どんなプログラム言語も画面に表示させる関数は、時間を浪費します。

/*
プログレスバーで処理経過を表示
2009.4.28
*/


var i;
var loop = 100000000;//ループの回数を変数にしました。
var tm = time();

for(i = 0 ; i < loop ; i++)         //ループカウンターをループ回数で割っています。
        StatusSetBar(i/float(loop));//そのままの整数の割り算をすると0になるので分母を実数に変換しました。

println((time() - tm)/1000.0);

38.310001

プログレスバーを追加しただけで、カラのループより約3倍の時間が掛かりました。
これは、あくまでもカラのループの3倍で、ベクトルや平方根関数処理の3倍と言うわけではありません。

Progress_02

実行すると、プログレスバーが現れ青い帯が増えていきます。
終了しても表示されたままです。自動的には消えないのでした。

このプログレスバーを情報表示バーから消すには…

StatusClear();

を使います。

/*
プログレスバーで処理経過を表示/終了後消去する
2009.4.28
*/


var
i;
var loop = 100000000;
var tm = time();

for(i = 0 ; i < loop ; i++)StatusSetBar(i/float(loop));

println((time() - tm)/1000.0);

StatusClear();                      //プログレスバーを消去

これで、処理が終了するとプログレスバーが消えます。

上のスクリプトの様に、ループ処理の回数がわかっているfor()ときは良いのですが、while()の時の様に処理の終了がわからない時はStatusSetBar()ではなくて、

StatusSetSpin();

を使います。パラメータはありません。

/*
スピンバーを使用
2009.4.28
*/


var
i;
var loop = 100000000;
var tm = time();

for(i = 0 ; i < loop ; i++)StatusSetSpin();//barからspinへ変更しました。

println((time() - tm)/1000.0);

StatusClear();                      //プログレスバーを消去

StatusSetSpin()が実行されるたび帯が右にシフトします。

Progress_03_2

29.611000

パラメータがない分、Spinの方が少し速いですね。

では、このプログレスバーを追加して処理が遅くなるのを軽減するには…

C.O.F.F.E.E.にはインターバル割り込み処理がないようなので、カウンター変数から算出し比較して実行させてみる。
1000回に1回とか、10000回に1回と実行回数を減らしてみましょう。

/*
処理時間軽減の為にプログレスバーの更新を10000回に1回にする。
2009.4.28
*/


var
i;
var loop = 100000000;
var tm = time();

for(i = 0 ; i < loop ; i++){

  //ここに処理を記述


  if(!Modulo(i , 10000))StatusSetBar(i/float(loop));//10000回に1回実行させます
}

println((time() - tm)/1000.0);

StatusClear();                      //プログレスバーを消去

結果は…

35.917000

ループ回数全て実行しても約38秒…全く意味をなしていない。
計算して結果を比較するという処理をループの回数するのだから仕方が無いのか…
この他にも、シフト演算子なども試してみたけど、意味がありませんでした。
プログレスバーの遅滞の軽減は無理なのか…
ま、カラのループでプログレスバーを試しているので、本来のスピード比較にはなりませんけどね。

では、最後にプログレスバーの右側に処理時間(秒)を表示してみます。
C.O.F.F.E.E.SDKを一通り目を通しましたが、ミリ秒をHH:MM:SSテキスト変換関数が見当たらなかったので自作するしかないのですが…見落としが無ければ…
今回は単純にミリ秒を1000で割って秒表示にします。

情報表示バーにテキストを表示する。

StatusSetText([string]text);

を使います。

/*
プログレスバーの右にテキストを表示
2009.4.28
*/


var i;
var roop = 100000000;
var tm = time();

for(i = 0 ; i < roop ; i++){
  StatusSetBar(i/float(roop));
  StatusSetText(tostring((time() - tm)/1000));//処理の経過時間(ミリ秒)を1000で割りテキストに変換します。
}

println((time() - tm)/1000.0);

StatusClear();

結果は…プログレスバーは表示されたものの、青い帯は動かず…
5秒の所でカウントがとまったので放置しました。

297.743988

約5分か…

テキストのみ10000回に1回表示させてみると

/*
情報表示バーのテキスト表示に掛かる処理時間を表示する回数を減らして軽減してみる
2009.4.28
*/


var
i;
var loop = 100000000;
var tm = time();

for(i = 0 ; i < loop ; i++){

  //ここに処理を記述


  StatusSetBar(i/float(loop));
  if(!Modulo(i , 10000))StatusSetText(tostring((time() - tm)/1000));//10000回に1回実行させます
}

println((time() - tm)/1000.0);

StatusClear();

結果は…
秒カウントは、やはり5秒で止まりました。
プログレスバーの青い帯は少しだけ動きました。

64.092003

64秒か…5分よりましか…
でもカウントが止まるのはなぜだろう?自分だけかなぁ?
テキスト表示用のバッファに関係してるのかなぁ?
C4Dにイベント送れば良いのかなぁ?
処理時間が更に増えそうだけど…

/*
情報表示バーのテキスト表示に不具合があるのでC4Dにイベントを送ってみる
2009.4.28
*/


var
i;
var loop = 100000000;
var tm = time();

for(i = 0 ; i < loop ; i++){

  //ここに処理を記述


  StatusSetBar(i/float(loop));
  if(!Modulo(i , 10000)){
    StatusSetText(tostring((time() - tm)/1000));
   
EventAdd();//試しにイベントを送ってみる。
  }
}

println((time() - tm)/1000.0);

StatusClear();

何も変わらず…

新たな変数を使って1秒間に1回だけ文字を表示させてみる。

/*
情報表示バーのテキストの更新を1秒に1回にしてみる
2009.4.28
*/


var i;
var loop = 100000000;
var sec = 0;//処理時間(秒)の比較用
var
tm = time();

for(i = 0 ; i < loop ; i++){

  //ここに処理を記述


  StatusSetBar(i/float(loop));
  if((time() - tm) / 1000 != sec){//秒が変わったらテキストを表示
    StatusSetText(tostring((time() - tm)/1000));
    sec = (time() - tm)
/ 1000;//比較用変数の更新
  }
}

println((time() - tm)/1000.0);

StatusClear();

やはり5秒の所でカウント表示が止まるなぁ…

73.387001

処理時間も増えましたね。
使い方が間違っているのか…
もしかすると、こんな使い方するものじゃなく、単純にテキスト(説明や警告)を表示させるものなのか…

今回はファイルがありません。スクリプトマネージャにコピペして実行してみてください。

もう気付いているかも知れませんが、ループの間のC4Dの操作はできません。

|

« C4D XPressoでの水面のアニメーションをYouTubeにアップしてみました。 | トップページ | C.O.F.F.E.E.やユーザスクリプトから、C4D標準マテリアルのパラメータを調整する。 »