Mercurial > hg > Members > kono > Cerium
changeset 274:6ecb2b8a2505
merge
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Thu, 04 Jun 2009 00:16:56 +0900 |
parents | 3e9c0039e15c |
children | 8622eae386d1 |
files | TaskManager/ChangeLog example/HelloWorld/ppe/.#Hello.cc include/TaskManager/Task.h |
diffstat | 2 files changed, 1933 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TaskManager/ChangeLog Thu Jun 04 00:16:56 2009 +0900 @@ -0,0 +1,1888 @@ +2009-02-13 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * kernel/ppe/Random.cc (reset): fix + urandom -> random とどれも読めなかったら + gettimeofday() での時間から seed を求めるように + +2009-02-12 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: kernel/ppe/Random.cc + 乱数生成クラス。 + ゲームだとユーザ使うでしょうきっと。 + 一応 /dev/random から seed 取る様にしてます + +2009-02-04 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * kernel/ppe/TaskManager.cc (TaskManager::allocate): rename + malloc -> allocate + + * kernel/main.cc (main): fix + cerium_main を呼ぶのではなく、TMmain という名前にしました。 + ちょっと SDLmain をパクった感じで。 + まあ TaskManager の main で cerium_* って名前は微妙に変だからね。 + + * kernel/ppe/TaskManager.cc (TaskManager::set_TMend): add + cerium_main があるんだから、cerium_end があってもいいじゃない。 + もっと言うと、TaskManager に main を隠すって流れなんだけど + 終了を検知できないのはちとやりづらいかなと。 + たとえば測定とか。Task の post_func とかでもやれないことはないけどね。 + というわけで、ユーザが、プログラム終了時に呼ばれる関数を設定できるように。 + +2009-01-20 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/spe/SchedTask.cc (SchedTask::get_cpuid): add + printf デバッグ時に、どの CPU かって知りたい時があるので。 + + PPE = 0 + SPE = 0〜spu_num-1; + + PPE は 0 以外に分かりやすい数字がいいんだけどなー。SPE と被るし。 + -1 とかはエラーっぽいから好かない。まあいいんだけど。 + + User Task では以下の様に使用します + + int cpuid = smanager->get_cpuid(); + + + * Cell/SpeThreads.cc (SpeThreads::spe_thread_run): fix + SPE_EXIT が出る時は正常終了だけど、これだと + エラーでたようなメッセージに見えてしまう(俺がそう見えてしまった)ので + ここは表示しなくてもいいかな。 + + * kernel/ppe/MailManager.cc (MailManager::destroy): fix + 無限ループになってた。この for() 間違ってるかな。 + なんか TaskQueueInfo.cc とかでも、結局 while() に直してるし。 + + * kernel/ppe/TaskManager.cc (TaskManager::~TaskManager): add + kernel/main.ccで + + delete manager; + + としているのに、TaskManagerImpl::~TaskManagerImpl が呼び出されず + どうしてかなと思ったら、そもそも ~TaskManager が無かった。あほか + +2009-01-05 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * all : fix + Scheduler::curIndex_taskList を削除し、 + SchedTask に持たせる様に変更。(SchedTask::__cur_index) + それに伴い、SchedTask::__init__() も cur_index を入れる様に変更 + +2008-12-24 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * kernel/schedule/SchedTask.cc (SchedTask::ex_init_renew) + (SchedTask::ex_init_normal): add + (SchedTask::__init__): fix + + init でも ex_init を使える様に。 + あと、コンストラクタで渡していた引数を __init__() に渡す様にした。 + コンストラクタの引数あると、継承する時にいちいち親クラスのも書かないと + いけなかった。これ省略できないんだよな。めんどくさい。 + + 例. + class Hoge : public SchedTask { + Hoge(int i) : Task(i) {} + }; + + なので、今までは Scheduler.h に SchedConstructor ってマクロを書いて + クラス名入れるだけで上の様な形になるようにしていた。 + でも、例えば + + SchedTask -> Hoge -> Fuge っていうように Fuge ってタスクを + 作りたいとき、上のままだと SchedTask に引数渡してしまうのでだめ。 + もうめんどくさいってことで、コンストラクタ全てデフォルトにして、 + __init__() の引数に渡す様にしました。 + + (SchedTask::__set_renewFlag): add + + ここで、PPEで生成されたか(normal)、SPE で生成されたか(renew) の + 判定を行い、ex_xxx の設定もする + + (SchedTask::get_inputSize, SchedTask::get_outputSize): add + + アドレスだけじゃなく、そのサイズも取れた方がいいだろう + + +2008-12-23 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/spe/SchedTask.cc (SchedTask::get_outputAddr) + (SchedTask::get_inputAddr): add + + in/out のデータだけじゃなく、そのアドレスも取れた方がいいだろう + +2008-12-22 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/spe/SchedTask.cc (SchedTask::__init__, SchedTask::read) + (SchedTask::exec, SchedTask::write): fix + (SchedTask::ex_read_normal, SchedTask::ex_read_renew) + (SchedTask::ex_exec_normal, SchedTask::ex_exec_renew) + (SchedTask::ex_write_normal, SchedTask::ex_write_renew): add + + SPE 内で生成されたタスクは、PPE で生成されたものと違い + + - add->inData + : PPE から DMA or SPE 内のものをそのまま使う + - PPE にタスクが終了したことを知らせる + : 生成されたタスクを待つ必要があるなら、その時点では送らない + + とか、まあいろいろ処理が違うわけです。 + そして、タスク内生成タスクの判断をする + + __flag_renewTask ? 0 = PPE で生成 : 1 = SPE で生成 + + という変数がある。これでいくつか処理を分けてるんだけど、 + 今までは + + if (__flag_renewTask) { + } else { + } + + ってやってた。これではいかんという事で、 + __init__() 内で、関数ポインタに、 + + ex_xxxx_normal: PPE で生成されたタスクに対する処理 + ex_xxxx_renew: SPE で生成されたタスクに対する処理 + + と入れて、if 文無しでやってみた。 + 今は ex_write_xxx しか書いてないが、これからread/exec でも + 出てくると思うので、作っておいた + + +2008-12-19 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/spe/CellDmaManager.cc (CellDmaManager::dma_wait) + (CellDmaManager::mail_write, CellDmaManager::mail_read): fix + writech、readch の関数を、wrap (って言い方でおk?)された関数に変更。 + 最適化掛かってるっぽいし、長いよりはわかりやすいし。そのための wrap。 + + 例: + - before + spu_readch(SPU_RdInMspu_readch(SPU_RdInMbox); + - after + spu_read_in_mbox(void); + +2008-11-05 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: Task 内での API + Task 外での API は、今まで通り manager->create_task とかですが + タスク内でも、「オブジェクト->関数」の呼び出しがいいんじゃないか + って話になったので、付け加えました。今のところ、SchedTask.h の + 内部クラスとして + + STaskManager + + ってのを加えて、ユーザはそのインスタンスである + + smanager + + からAPIにアクセスします。 + 今までは __scheduler->dma_load とかいろいろやってたんですが + これからは全て smanager にしました。 + というわけで、ここに使える API 一覧。いずれゲーム班 wikiの方にも。 + + - get_input, get_output, get_param + - create_task, wait_task + - global_alloc, global_get, global_free + - mainMem_alloc, mainMem_wait, mainMem_get + - dma_load, dma_store, dma_wait + - allocate + + 使い方は追々描きますが、 + 今のところ上に変更しなくてもそのままの記述で動くはずです。 + いずれは全て移行してもらうことになりますがきっと。 + + + * kernel/schedule/SchedTask.cc: + いろいろ関数が増えてますが、ラッパーです。 + +2008-11-01 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: kernel/main.cc + main loop をユーザに書かせるのはめんどくさいので、 + ライブラリ側で main() を書く事にしました。 + ユーザ側では main() の代わりに cerium_main() を + 書かせるようにしています。引数は main() のをそのまま渡す感じで。 + + Cerium 標準のオプションとして、-cpu は付けました。 + ゲームフレームワークってことで、-width とか -height は + 標準でつけてもいいかなって話なので、これは後日実装。 + 標準オプションで受け取った値にアクセスする方法も考えないと。 + manager->cpu とか manager->width とかは安易か? + + * add: Cell/PpeScheduler.cc + MainScheduler をそのまま使うと、 + PPE のタスクで mainMem_alloc で確保した領域がアライメント + 取れていないため、SPE で使うと余裕でバスエラー。 + + Scheduler->allocate で poxis_memalign で使えるように。 + + * move: kernel/schedule/FifoDmaManager.cc, MainScheduler.cc + kernel というよりは Fifo バージョン用なので Fifo/ に移動。 + + +2008-10-21 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * kernel/ppe/TaskManagerImpl.cc (TaskManagerImpl::systask_init): fix + 下に述べてる SysTask_Finish を regist する部分 + + (TaskManagerImpl::spawn_task): + + SysTask_Finish に対して、タスクが spawn されるたびに + wait_for を掛けて、待つようにしている。 + + * add: kernel/systask/ + 久々の更新乙 + + プログラム動かすとき、タスクが SPE だけで、 + PPE で待ってるタスクが無いとそのままプログラムが素通りするってことで + 今まではユーザに、全てのタスクを待たせるタスクってのを書かせてた。 + まあもちろんめんどくさいので、いい加減追加した。 + + system task っつーことで、spawn された全てのタスクを待つ + SysTask_Finish を作った。これでいちいち task_finish とか作らなくておk + +2008-08-10 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * thinking: add_update() ? + 現在、タスクは input/output があるわけですよ。 + で、例えば + + - 入力データ : PolygoPpack + - 出力データ : SpanPack + + ってなわけですが、別のタスクで + + - 入力データ : SceneGraphPack (更新前) + - 出力データ : SceneGraphPack (更新後) + + ってのがある。つまり Update なわけだ。 + 今のところ、同じアドレスを add_inData, add_outData に設定して + タスク内で memcpy(wbuf, rbuf, sizeof(SceneGraphPack) とかしてる。 + まあそれはそれでいいのかわるいのか。 + + in/out だけじゃなくて update も必要? + +2008-08-08 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: ../include/TaskManager/base.h + 通常の new/delete では、RTTI とか 例外処理とかで + -fno-exceptions や -fno-rtti をコンパイルオプションにしても + 効かなかったんだけど、operator new/delete をオーバーライドして + 中身を普通の malloc/free にすると、上の処理が無くなるので + オプションが効くようになる。結果コードサイズも減ると。 + SPE の場合、70〜80KBは減りました。使わない手は無い。 + つーことで、一応動いてる。。。といいたけど動いてないorz + 最適化 (-O2 とか -O9) をかけると止まる。SPE 上でね。 + FIFO バージョンだと問題ない。SPEだとだめだ。 + 今わかってる、止まる場所は Scheduler::run() 内の + + task3->write(); + + だ。task1~3までのnewは(多分)できているんだけど + そこを呼び出すと SPE 自体が終了してしまう。謎だ + + 一応、俺作の new/delete は base.h に定義してあって、 + 通常の API との切り替えは、base.h にある + BASE_NEW_DELETE を切り替えるだけでおk。 + 全てのファイルではなく、現在は SPE で使いそうなところだけやってます。 + いずれは全部やったほうがいいかな〜 + + ライブラリ側の最適化はアウトだけど、ユーザ側では問題ないです。 + なので、今は + + ライブラリ側(libspemanager.a)は最適化無し(-O0) + ユーザ側(SchedTaskを継承したやつね)は最適化しても無問題 (-O9) + + でっす。ここらへん完璧なれば、だいぶ楽になる。 + つーかもう C++ やめ(ry + + +2008-08-07 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * change: mainMem_set -> mainMem_wait + allocate を待つんだから、なんとなく wait かな。 + あと、ユーザも使えるので、wait の方がわかりやすいと思ったり。 + +2008-08-05 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: mainMem_alloc, mainMem_set, mainMem_get + SPE から メインメモリの領域に対して allocate できないと + SceneGraphの生成やら、結構クリティカルな処理を + 全部 PPE でやらないといけなくなるってことで実装しました。 + + 流れとして + + 1 タスク中に、mainMem(id,size) を実行する事で、 + メインメモリに対して allocate のコマンドを発行。 + + 1.1 Scheduler から PPE に対して + - commmand (MY_SPE_COMMAND_MALLOC) + - id (PPEから戻ってくるコマンドに必要) + - size + を mailbox で送る + + 1.2 確保した領域はそのタスク内では取得できない(NULL が来ます) + 正確には、返事の mail をここでは read してないから + + 2. PPE では、受信した mail が MY_SPE_COMMAND_MALLOC だったら + 次に来る mail が id と size であるとして read を行い、 + size を元に allocate する。allocate が完了したら + - id + - allocate された領域のアドレス + を SPE に mail で送る + + 3. SPE Scheduler では、SchedTaskList::read で、 + 一つ前の TaskList 中で実行された mainMem_alloc の数だけ + PPE からのメールを待つ。mainMem_set() の処理です。 + + 4. create_task されたタスク内で mainMem_get(id) とすると + allocate したメインメモリ領域のアドレスが得られる。 + + こんな感じ。結構ださい実装なので、もうちょいスマートにいきたいよね。 + 例題は Game_project/student/master/gongo/MainMemMalloc にあります。 + README にもおんなじこと書いてます。 + + * memo: The number of available entries of Inbound/Outbound Mailbox + Outbound (SPE -> PPE) のmailboxデータキュー保持数は + + /* SPE プログラム中 */ + #include <spu_mfcio.h> + spu_stat_out_mbox(void); + + で調べる事が出来る。 + + --- 記述例 --- + printf("Available capacity of SPU Outbound Mailbox\n"); + printf(" %d\n", spu_stat_out_mbox()); + + --- 実行結果 -- + Available capacity of SPU Outbound Mailbox + 1 + + Inbound (PPE -> SPE) の mailbox データキュー保持数は + + /* PPE プログラム中 */ + #include <libspe2.h> + spe_in_mbox_status(spe_context_ptr_t); + + で調べられます。 + + --- 記述例 --- + printf("the number of available entries = %d\n", + spe_in_mbox_status(spe_ctx)); + + --- 実行結果 --- + the number of available entries = 4 + + Outbound が少ないなー。 + In/Out 共に、キューが MAX の場合、減るまで wait 掛かるんだよな。 + それがどこまで影響あるかは実際にやらないと、ってことか。 + + * fix: ファイル名の変更 (*.cpp -> *.cc) + 前々から先生に直せ言われてたので。 + + cvs のファイル名を変える方法は簡単に二つ(てかこれだけ?) + + 1. cvs rm hoge.cpp; cvs add hoge.cc + 2. リポジトリを直接変更 mv hoge.cpp,v hoge.cc,v + + めんどくさかったので 2 でやりました。 + Attic (削除されたファイルがあるリポジトリディレクトリ?)にも + 同じ処理を行えば、tag で update かけてもちゃんと反映されました。 + +2008-07-22 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * tag: open-campus-2008 + 次やる事は、Cell/spe 以下のコードサイズを減らすために + new/delete を消して malloc/free で統一する事。 + placement_new ってのを使えば、コンストラクタは呼べて + new ほどサイズ圧迫しないからこれにしようかな。 + 逆の placement_delete ってのは自分で小細工しないと行けないらしい。 + まあ、これが旨く行けば 80KB ほど減るから。やるべきだろう。 + + * Cell/spe/Scheduler.cpp (Scheduler::dma_load): アホなミスその2 + 自分で __scheduler->dma_store をやってもデータが送れない。 + そんな馬鹿な。つーことでいろいろ調べてもわからない。 + アドレスやサイズが違うのかと調べても違う。 + こうなったらっつーことでライブラリに printf 加えてみたら表示されない + あれ、おかしいな。たしかに Connector::dma_store に加えたはz・・ + + Scheduler::dma_store(void *buf, uint32 addr, uint32 size, uint32 mask) + { + <<< + connector->dma_load(buf, addr, size, mask); + ======== + connector->dma_store(buf, addr, size, mask); + >>> + } + + なぜ store から load を呼んでるのか不思議だった。 + Scheduler::dma_load をコピペして dma_store にした後、 + 中の connector->dma_load を変えなかったってオチだな。 + 下のミスと合わせて5,6時間費やしたよHAHAHA + + * Cell/spe/SchedTask.cpp (SchedTask::exec): アホなミスその1 + Test/test_render で、 + SpanPack のデータが時々壊れてる感じがする。 + 送る前までは正常だから生成に問題は無いはず。 + つーことでいろいろ調べたがわからず。 + printf デバッグすると動く不思議 + なんだ、printf で遅くなったらできるってことは + DMA が完了する前に SchedTask::run にきてんのか? + いやいや、そんなばかな。だってちゃんと wait し・・・ + + <<< + ============ + __scheduler->dma_wait(DMA_READ); + >>> + + はいはい wait し忘れ wait し忘れ + +2008-07-16 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * memo: if 文消した成果2 & memcpy するかしないかか + Renew Task では、inListData,outListData は新たに allocate して + 使っているので、SchedTask にそって実行する場合、 + + __scheduler->dma_load(__inListData, (uint32)__task->inData, + sizeof(ListData), DMA_READ_IN_LIST); + __scheduler->dma_load(__outListData, (uint32)__task->outData, + sizeof(ListData), DMA_READ_OUT_LIST); + + の代わりに + + memcpy(__inListData, __task->inData, sizeof(ListData)); + memcpy(__outListData, __task->outData, sizeof(ListData)); + free(__task->inData); + free(__task->outData); + + もしくは + + __inListData = __task->inData; + __outListData = __task->outData; + (__task->inData と __task->outData は Destructor で free する) + + とやっています。 + memcpy が重いのはわかるんですが、下の方法では + Destructor で if 文使って free() しているわけです(このタスクが Renew か否か)。 + ですので、どっちが早いか試してみた。 + + /** + * memcpy() して、すぐ free() する version + */ + void + test_cpy(int flag, int *src) + { + if (flag) { + memcpy(data, src, sizeof(int)*length); + free(src); + } + } + + /** + * 参照で扱って、最後に free() する version + */ + void + test_nocpy(int flag, int *src) + { + if (flag) { + data = src; + } + + // この部分を SchedTask::~SchedTask() と + // 思ってください + if (flag) { + free(data); + } + } + + + これらの関数を10000回ループしました。 + src の allocate は関数の外でやっており、その部分は実行時間に含まれてません + flag は 1 or 0 の繰り返しです。 + + - 実行結果 (1) + :no copy + SPE time by SPU Decrementer: 0.035500 + :copy + SPE time by SPU Decrementer: 0.057500 + + memcpy しないほうが速いらしいです。 + ためしに、flag を ずっと 1 にしてみました。 + + - 実行結果 (2) + :no copy + SPE time by SPU Decrementer: 0.055250 + :copy + SPE time by SPU Decrementer: 0.053389 + + 今度は copy するほうが早いという不思議。 + でもまあ、ずっと 1 ってことはないと思いますし、 + むしろ flag == 1 になるほうが少ないと思うので、 + no_copy version でやったほうがいいかな。 + + おまけで、実行結果 (1) の環境で、test_nocpy を変えてみた + + void + test_nocpy(int flag, int *src) + { + if (flag) { + data = src; + } + + free((void*)(flag*(int)data)); + } + + キャストしまくりですが、単純に free(flag*data) だと + 「invalid operands of types 'int' and 'int*' to binary 'operator*'」って + 出るので、キャストで逃げました。 + で、実行結果なんですが + + - 実行結果 (3) + :no copy + SPE time by SPU Decrementer: 0.040375 + :copy + SPE time by SPU Decrementer: 0.059500 + + 遅くなってーら。キャストが悪いのか。乗算が重いのか。 + branch が無い? spe の if 文と対決しても遅いのかー。 + 例題が間違ってる可能性もあるが・・・ if 文は使っていくかなー + + +2008-07-10 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * fix: TaskGroup->group + 今まで slist っていう、ライブラリの単方向リスト構造体?を + 使ってたんだけど、まあいろいろあって、TaskQueue を使うようにしました。 + 最初からこれにするつもりではあったけどね。 + RenewTask や static_alloc とかの実装を優先したので + ライブラリを使いました。といっても、書いてみると + それほと記述量無いので最初から行っても良かったかなーと思ったり。 + + そんなわけで動いてます。つーか、やめてよかったよ slist。 + slist を使ったやつと使ってない奴のファイルサイズがやばい + + -rwxr-xr-x 1 gongo gongo 120672 2008-07-10 14:29 spe-main* + -rwxr-xr-x 1 gongo gongo 180368 2008-07-10 13:40 spe-main.bak* + + .bak が slist を使ってる、上のやつが使ってないversionです。 + まさか 60k も違ってくるとは思わなかった。 + SPE LS の容量が 256k と考えると、かなりの痛手だったよ。アブねえ。 + インラインとか最適化掛けまくってて、コード量が増えてるからかなー。 + + 「SPU C/C++ 言語拡張」とかで、C++ のライブラリがSPUでも使えるよ〜 + って書いてたから入れてみたんだけど。罠だったか。 + おそらく SPU に移植した側の人も「サイズが増えるのを覚悟で使え」って + ことだったんだろう。なかったら文句言う人も居そうだし。 + +2008-07-09 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * fix: TaskGroup での task の扱い + 下にもかいているけど (直したいところ (1)) + TaskGroup->group が持つ要素は int で持ってて、 + それらは、同じく TaskGroup が持つ cur_id をインクリメントしていって、 + それを要素としていました。つまり、TaskGroup->group は、厳密にいえば + 「どの Task があるか」ではなく、「いくつのタスクがあるか」を + あらわしているだけでした。slist を使う意味もなかったわけです。 + + そこで、SchedTask が持つ、RenewTaskList の解放のタイミングを + RenewTaskList の一番最後のタスクが delete されるときにしました。 + これによって、アドレスが被ることがなくなったので + TaskGroup->group の要素を TaskPtr にできました。 + この方が、TaskGroup の意味的にもしっくりくるのでよかばってん。 + + * memo: if 文消した成果 + + #ifdef FREE_TEST + free((ListDataPtr)(__flag_renewTask*(int)(__inListData))); + free((ListDataPtr)(__flag_renewTask*(int)(__outListData))); + free((TaskListPtr)(__flag_renewTask*(int)(__list))); + #else + if (__flag_renewTask) { + free(__inListData); + free(__outListData); + free(__list); + } + #endif + + こんな感じで、いくつかか if 文を消してみた。 + そして、PPE側の main.cc で gettimeofday で計測してみた (各10回) + + + - if 文消した場合 + time: 1.222000 + time: 1.230000 + time: 1.241000 + time: 1.230000 + time: 1.223000 + time: 1.257000 + time: 1.219000 + time: 1.228000 + time: 1.220000 + time: 1.229000 + avarage: 1.2299 + + - if 文消してない場合 + time: 1.225000 + time: 1.215000 + time: 1.229000 + time: 1.218000 + time: 1.223000 + time: 1.214000 + time: 1.225000 + time: 1.215000 + time: 1.224000 + time: 1.219000 + avarage: 1.2207 + + あまり変わらな(ryむしr(ry + 使い方がまずいのか、もっとと回数を増やせば変わってくるのかね。。。 + PPE でなく、 SPE のほうで計測すべきなのかなーとか思ったり思わなかったり。 + + +2008-07-08 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: Renew Task の wait + Renew Task は今まで「生成されたやつ全部待つ」だったのを + + void SchedTask::wait_task(TaskPtr task); + + ってのを作って、任意のタスクに wait 掛けれるようにしました。 + 名前が思いつかなかったお。。。 + 動作確認済み・・・だと思います。例題・・・誰か例題を!(俺が + + + * fix: SchedTask の変数名 + ユーザが継承して使う SchedTask クラスなんですが、 + 今まで変数は list, task などを使ってました。 + が、これは一般に使われやすい変数名です。 + その証拠に、俺も例題書いている時に task って名前が被ってました。 + + run(r, w) + { + ... + + //TaskPtr task; <= 宣言してないのにエラーにならない + task = create_task(TASK_EXEC); + } + + ってコードを書いてたせいで、Scheduler が使用する task を + 上書きしたせいでバグってました。ってことがありました。 + 上のように、宣言してないのに普通に通ってるのを気づきませんでした。 + 今のところ変数名は __task とか __list にしてあります。 + private にしてもいいんだけどさ。 + + +2008-07-07 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * fix: if 文を無くしてみた + 下の方に 「if () が多い」って書きましたが、いろいろ小細工を。 + SchedTask をやってみました。例えば + + if (cmd != 0) { + delete taskGroup; + scheduler->mail_write(cmd); + } + + ってのがありました。cmd ってのは taskGroup->status で + もし cmd が 0 でなければ、taskGroup はすでに空っぽで + 待つべきタスクはすべて終了したので、taskGroup を delete し、 + mailbox で cmd を PPE に送ります(cmd にはすでに送るべきコマンドがある) + でまあ、これくらいなら + + delete (int*)((cmd != 0)*(int)(taskGroup)); + scheduler->mail_write(cmd); + + ぐらいに直せました。 + delete や free では NULL を渡しても何もしない(?)って動作なので + これでも問題ない。つまり、cmd == 0 なら、taskGroup を + 解放する必要は無いので NULL が delete に渡されるわけです + int* でキャストしてるのは、そのまま 0 を渡すと、 + 「int型を delete するのはできない」的なエラーがでるからです。 + 。。。だったら int* じゃなくて TaskGroupPtr じゃね?とか思った今。 + + あと、PPE 側で 「mail == 0 なら NOP」 的な処理を入れました。 + これによって、cmd が 0 かその他で if を書く必要がなくなりました。 + 問題があるとすれば、 SPE -> PPE の mailbox の queue の長さ。 + NOP コマンドを送って、queue の制限に引っかかって + mail_write が止まるんじゃないかなーとか少し心配です。 + ここらへんは optimize の時間に考える事かな。 + どうせ PPE では mail しか読んでないし、 + そこまで queue が埋まる事は無いと思いたい。 + + + + あとはこんな感じかな + + #if 1 // fix + free((void*)(flag_renewTask*(int)(list))); + #else + if (flag_renewTask) { + free(list); + } + #endif + + 動いてるのは確認したし、gdb で x/20i とかしたら + branch 命令が減ってるのは確認した。 + まあ -O9 とかで最適化掛けるとどっちも同じになるけどな。 + + + * add (API): static_alloc, static_get, static_free + SchedTask 自身だけが持つ領域ではなく、 + SPE 上に複数のタスクが共有したい領域を作る + これは task::run() 内で使用する。 + + - void* static_alloc(int id, int size); + @param [id] 領域ID。現在は 0〜31 まで使用可能 (Scheduler.h で定義) + @param [size] 領域のサイズ + @return allocate した領域のポインタ。下の static_get の返り値と同じ + + - void* static_get(int id); + @param [id] static_alloc で作った領域 ID。 + @return 領域のポインタ + + - void static_free(int id); + @param [id] 解放したい領域の ID + + こんな感じかなー。 + static_free はさすがにユーザに任せるだろう。 + static_free し忘れると SPE には致命的なので、ここはよく伝える必要有 + + 例題は + cvs: firefly:Game_project/student/master/gongo/Static + + まあ Renew と大体同じですけどね。 + int 型配列 data を共有にして、各タスクでインクリメントしてる + + * TODO: TaskGroup の扱い + 通常の Task では、task->self には + 自分が終了した時に PPE に送るコマンド(自分自身)になりますが、 + タスク中に生成されたタスク(もう何度も書くのめんどいんで Renew で)では + task->self は、task を待っている TaskGroup を表します。 + + self という名前で意味が違うのでこういうことはやめたいんだが。。。 + といいながらやめないのが(ry + + * memo: + 下の 直したいところ (1) ってやつがよくわからんので、 + 現在の状況だけ + + scheduler->add_groupTask() をするたびに + + group.insert_front(cur_id++); + + されます。 + そして、scheduler->remove_groupTask() されると + + group.remove(--cur_id); + + されます。要するに、どのタスクでも + cur_id だけが insert/remove されます。 + 「どのタスクがあるか」ではなく「どれだけのタスクがあるか」ですね。 + 実際にはしっかりと TaskPtr で管理したかったんですが、 + 下にも書いたアドレスが被る云々の問題でそれもできず。 + やり方はあると思うんですが。 + + うーん、うまく説明できないな。 + + * tag: v20080707 + タスク内タスク生成を作りました。 + + [TODO] + SPE 上で領域を共有する API の + + - static_alloc + - static_get + - static_free + + を速攻で実装しよう。。 + + * add: タスク内タスク生成 + 一応できたんですが、直したい。。。 + 仕様としては + + - 現在のタスク(T1) の中でタスクを生成したとする (Tc = T2, T3, ...) + - 本来、T1 が終了次第、T1 が終わった事を PPE に伝えるが、 + ここでは、Tc が全て終わってから、T1 の終了を PPE に伝える + - Tc 内で再びタスクが生成されても(Tcc)、Tcc が終わってから T1 を(ry + + 現在は、生成したタスクすべてに対して wait_for をかけてる感じ。 + しかし、例えば Frame Buffer に書き込む時は待つ必要ない(はず)なので + タスク毎に wait_for を選べるようにした方がいいだろう。 + + __ 例題 + cvs firefly:Game_project/student/master/gongo/Renew + + にあります。 + もうちょいちゃんとした例題が欲しいところです。 + + + __ 直したいところ (1) + + 現在、Tc を管理する構造体として、TaskGroup を使ってます + + class TaskGroup { + unsigned int command; // T1 が PPE に送るコマンド + __gnu_cxx::slist<int> group; // Tc がある Linked List + + // function は省略 + }; + + slist じゃなくて、TaskQueue みたいに自分で作っても良かったんだけど。 + group.empty() == true になったら、command を PPE に送るって感じです。 + + で、slist が持つデータが TaskPtr じゃなくて int の理由。 + まあいろいろあるんだけど(何)、アドレスが重複してしまうことです。 + 最初は、create_task で得られた TaskPtr をキーとして使うつもりだったけど + その TaskPtr は TaskList から取った物で (&list->takss[index] みたいな) + なんでそれじゃだめなのか。buff_taskList[2] (Scheduler.cpp 参照) を + 使うと、交互に使用するのでアドレスは被る。 + 新たに allocate すれば問題は無いが (t1とする)、SPE の LS の問題で + 使わなくなった TaskList は free していかないといけない。 + で、free -> 再び allocate したとき (t2とする)、t1 と t2 の + アドレスが被ることがあった。当然 TaskPtr も被ると。 + だから、アドレスではなく、TaskGorup が持つ + unsigned int cur_id を使う事にしました。 + + なんかここまで自分で書いてて、 + なんで出来ないのかまだわからんくなってきた。 + + ので試しに戻してみたら * で * き * ま * し * た * + わけわからん。まあ勘違いだったのか、いろいろ別のところを直してるうちに + 知らず知らずミスってたところも治ってたのか。まあいいか。 + + と思っていろいろ試したらまた動かなくなった。。もうだめぽ + とりあえず、また unsigned int に戻しました。 + 今のところ、0 <= cur_id <= 0xffff (65535) の範囲のキーを使うように。 + + + __ 直したいところ (2) + if 文が多い。 + 今は、「通常の Task」「タスク内で生成されたタスク」で挙動が違います。 + 例えば + + - SPE で allocate されたデータを使うので、通常 DMA を使うところは + アドレス参照や memcpy を使う + - TaskGroup を、上記の Tc や Tcc へ引き継がせるところ + + なので、flag_renewTask とかいう変数で、ほぼ if 文 で書いてます。 + SPE でこの書き方はかなりまずい気がします。良い書き方はないものか。。。 + 「通常の(ry」「タスク内(ry」で新たにインスタンスを作るってのも + 考えはしましたが (SchedTask = 通常、SchedRenewTask = タスク内(ry とか) + これだと ユーザー側も この二つを選んでやることになります。 + 「このタスクは SchedRenewTask 、このタスクは通常」とかやるのは + かなりめんどくさいと思う。だからライブラリ側で分けるべきか。。。 + 多重継承とかってこんなとき役に立つん? + + +2008-07-03 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * TODO: + - add_param で渡せるパラメータの数を増やす。15もあればいいんじゃね? + - 今の実装では、 + + 1. PPE でタスク(T1)が生成される + 2. SPE で T1 が実行される + 3. T1 が終わった事を PPE に mailbox で送る + 送る情報は T1 自身。(PPE で生成された時のアドレス) + + なわけです。しかし、もし T1 から新たにタスクが生成された時はどうするか + 仮に T1 から T2, T3, T4 が作られたとする。 + このとき、 + + 1. T1 が終わった時点で、T1 から終了コマンドを送る + 2. T1 だけでなく、T1 内で作られた T2, T3, T4 が終わってから + 終了コマンドを送る + + の二つが考えられる。 + PPE 側では T1 しか認識していないため、この判定は SPE 内でやることになる + 必要な処理かと言われると微妙だが、欲しくなるのは間違いない。 + つーことで今これを実装中です。 + + + * tag: v20080703 + - タスクに 32 bits パラメータを渡す add_param を実装(現在は3個まで) + - SPE 内部でタスク生成ができるようになった + + * add (API): SPE内部での create_task + 今まで、SPE ではタスクを生成する事は出来ず、 + PPE から送られてくるタスクを実行するだけでした。 + それだと不便だってことで SPE 内部でもできるようにしました。 + 方法はPPEでやるのと同じく + + task = create_task(TASK_EXEC); + task->add_inData(buff, sizeof(Buff)); + task->add_param(data); + + みたいな感じでいいです。 + spawn() や wait_for() は実装していません。 + SPE 内部で生成するタスク同士で依存関係作るのが + 結構めんどくさいからです。spawn() も、しなくても勝手に実行します。 + PPE とそろえる意味で作ってもいいんだけどね。 + そのためには SPE にも TaskManager が必要になってくるなー。 + + +2008-06-24 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add (API): add_param, get_param + DMA で送れないけど、必要になってくる 4 バイトの情報があるとして + それは今までは + + add_inData(param, 0); + + とかして、「サイズ == 0 なら 32 bit のデータ」としていたけど + それは余りにも変なので(関数の意味的にもおかしい)ので、 + + add_param(parameter); + + ってのを追加しました。タスク側では + + get_param(index); + + とかします。index は、add_param を呼び出した順番で決まります + + add_param(x); + add_param(y); + add_param(z); + + とあるとき、タスク側では + + int x = get_param(0); + int z = get_param(2); + + とします。 + 今のところ parameter は 3つしか送れないことになってますが + 後ほど、上限をあげます。15くらいあれば余裕だと思うんだがどうだい? + 今は、SPE でのタスクの生成のルーチンを書くために、最低限な部分だけ + ってことで 3 つにしてます。それが出来次第、これもやります。 + + +2008-06-12 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/CellTaskManagerImpl.cpp (CellTaskManagerImpl::set_runTaskList): + アホなミス(ry + + 「list が持つ TASK_MAX_SIZE を超えると、次の list へ next を」っていう + 前回直したところがまたミスっててだな。 + 簡単に言うと + + TaskPtr task = &list[list->length++]; + [task の初期化] + + if (list->length > TASK_MAX_SIZE) { + [newList 生成] + newList = append(newList, topList[speid]); + topList[speid] = newList; + } + + ってやってたわけ。これだと、toplist[speid] に + length = 0 の list が来る可能性があると。 + で、spe に TaskList を送る条件は + + 1. taskList[speid]->length >= 1 + 2. speid が次の TaskList を待っている状態 + + で、1 の条件に触れてしまい、TaskList が送られなくなって + プログラムが終了しないと。アホですね〜 + 上の if 文を &list[list->length++]; の前に持って行くだけでおk。 + +2008-06-10 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/CellTaskManagerImpl.cpp (CellTaskManagerImpl::set_runTaskList): + アホなミスしてました。 + list が持つ TASK_MAX_SIZE を超えると、次の list へ + next を繋げるはずなんだけど、speTaskList_bg[speid] とか読む時に + ちゃんと繋げられてなかったというかなんというか。 + 簡単に言うと、タスク多くなると落ち(ry + + * add (API): set_post + + create_task(id, 0); + + とかわざわざ 0 付けるのもアレなので、もうそれように + + task->set_post(func) + + を追加しました。func は void (*func)(void) です。 + せっかくだから、引数に void* とか付けてもいいんじゃないかと。 + + + * fix (API): ListDMA API + タスク側で、ListDMA で指定したデータの取り方 + + run(rbuf, wbuf) として + + // index は add_inData や add_outData で指定した(順番-1) + get_input(rbuf, index); + get_input(wbuf, index); + + 返り値は void* なので、malloc っぽくキャストしてください。 + あと、4バイト以下のデータを送りたい場合、main で + + add_inData(data, 0) + + と、アドレスは送りたいデータを則値で、サイズは 0 で指定するとおk。 + get_input で int なりなんなりでキャストすればいいじゃない! + 例題は + + Game_project/student/master/gongo/arr_cal + + で複数データ扱ってたり4バイト送ってたりしてます。 + + + * tag: v20080610 + 前回との違いは + + - ListDMA の導入 + - 凡ミスfix + + とかかな。何気にここには ListDMA の API 書いてなかったな。 + + - task->add_inData(addr, size); // input + - task->add_outData(addr, size); // output + + これで Input/Output のデータ領域を指定可能。複数できます。 + 詳しくはいずれドキュメントに書く予定だが、 + + - addr は 16 バイトアライメントに取れてないと行けない + - size は 16 バイト倍数 + + ってのが最低条件。 + 16 バイト未満のデータを送りたいとき(整数を2,3個とか)は考え中。 + addr に直接渡すって手法はできるとわかってるので、それでもいいかな。 + まあいろいろ問題はありますが、少しはできたんじゃないかな。 + + 次からは SPE 内でのタスク生成(再起動?)を書く予定 + + * Cell/CellTaskManagerImpl.cpp (CellTaskManagerImpl::set_runTaskList): + if (speid > machineNum) { + speid %= MAX_USE_SPE_NUM; + } + + から + + if (speid >= machineNum) { + speid %= machineNum; + } + + に。なんという凡ミス + + * Cell/spe/CellDmaManager.cpp (CellDmaManager::dma_loadList): fix + ListData が持つ ListElement は + + class ListElement { + public: + int size; + unsigned int addr; + }; + + というデータ構造なわけだが、これは、spu_mfcio.h が持っていて + 且つ List DMA で使用される + + typedef struct mfc_list_element { + uint64_t notify : 1; /** Stall-and-notify bit */ + uint64_t reserved : 16; + uint64_t size : 15; /** Transfer size */ + uint64_t eal : 32; /** Lower word of effective address */ + } mfc_list_element_t; + + と同じである。notify と reserved は 0 となる (ストールは今は + 考えていない)ので、結局は uint が 2 つの 8 バイト のデータ構造であれば + そのまま mfc_getl とか mfc_putl に遅れるわけである。 + 今までは mfc_list_element_t 構造体に for 文でいちいち代入してたが + まあそれはなくなったっつーことで。dma_storeList もね。 + + +2008-05-30 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * change (API): TaskManager Memory Allocate + manager->cerium_malloc(&buff, DEFAULT_ALIGNMENT, sizeof(Data)) + + から + + buff = (Data*)manager->malloc(sizeof(Data)); + + に変更しました。 + alignment の指定は全て TaskManager に埋め込んであります。 + 記述は TaskManager.h に書いてあります。 + + void* TaskManager::malloc(int size) { + return m_impl->allocate(DEFAULT_ALIGNMENT, size); + } + +2008-05-29 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * thinking: List DMA (4) + Cell 版でも動いたのを確認。今、Cell 版で List DMA が動く条件は + + 1. List の各要素の転送サイズが 16 バイト倍数でなければならない + 2. List の各要素の転送するデータのアドレスのアライメントを保証(16or128 + + 2に関しては Cell の仕様なんでまあいいんだけど、 + 1は、ドキュメント見る分には + + - Cell Broadband Engine アーキテクチャ version 1.01 より + - 7.5.3 get list + > リスト・サイズ・パラメータは、このDMAコマンドの場合は + > 8バイトの倍数でなければならず、また、リスト・アドレス・パラメータは、 + > ローカルストレージの8バイト境界にアラインされなければなりません。 + + って書いてるんだよな。int が 10 個の配列(40バイト) を送っても + 見事に弾かれたんだよな。おのれバスエラーめ! + とりあえず、上の条件を満たせば行けました。 + 送るデータのアロケートは + + TaskManager::cerium_allocate(void **buff, int align, int size); + + ってのを作りました。使い方は別項目で。だいたい posix_memalign 準拠。 + + 動くのはいいんだけど、これだとユーザに全部任せる事になります。 + 特に、配列をアロケートした後、その途中の部分をリストに入れたい時。 + その配列の要素のサイズが16倍数じゃないとそこでエラーがでると。 + それをユーザに全部任せるのは、まあいけないこともないけどさ。。。 + + + * Cell/CellTaskManagerImpl.cpp (CellTaskManagerImpl::mail_check): fix + CellTaskManager は FifoTaskManager のオブジェクトを + ppeManager という変数で持っていて、作業を別々に行っているわけで。 + だけど両方のオブジェクトがもつ waitTaskQueue は同じじゃないと + ならないので、最初は TaskQueuePtr * とかで渡して + 共有してたわけだけど、よくよく考えると、 + + - waitTaskQueue に task が append される時 + CellTaskManager->append_waitTask() + + - waitTaskQueue から task が remove されるとき(依存満たした時とか) + FifoTaskManagerImpl->mail_check() 及び + CellTaskManagerImpl->mail_check() です。 + + つまり、waitTaskQueue が共有されるのは mail_check だけなので、 + CellTaskManagerImpl の mail_check で + + ppeManager->mail_check(mail_list, &waitTaskQueue) + + として、ここで waitTaskQueue を参照渡ししてます。 + ppeManager->mail_check で waitTaskQueue の整理が終わって + 戻ってくる事には waitTaskQueue が更新されていると。 + + なんか文章がおかしいですね。気になる人は俺に直でお願いします。 + 要するに、ppe と spe のそれぞれの TaskManagerImpl で + waitTaskQueue の共有が上手くいったというわけです。 + + +2008-05-24 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * thinking: List DMA (3) + 現在実装中。Fifo 版では動いている模様。 + 問題は Cell だよなー。考えないと行けない事がいくつか + + - Input/Output データはアライメントされている? + アライメントされていなくても、こっちでアドレスずらして + DMAしてずらして run() に渡して〜とかもできるんだけど + かなりめんどくさい。それに、In ならともかく、 + Out は変な領域に書き込みそうなので無理そう。 + これはもうユーザが、送るデータはすべて + Cerium_malloc 的なものを通したものだけ、っていう + 制約にした方がいいかもしれない。てかそうなんだっけ。 + + - 配列中のデータの指定 + 上の項目と少し関連があるんだが、例えば + + int data[100]; // アライメントは取れてるとする + + ってのがあって、そのなかの data[0]〜data[49]、 + data[50] 〜 最後まで送りたいとする。 + 最初のやつは &data[0] のアドレスは 16 bytes アライメントだけど、 + &data[50] では、sizeof(int)*50 = おそらく 200 ずれて + 16 bytes アライメントではなくなると。これだと DMA できない。 + ユーザがそこまで考えて、例えば data[32] から送る、とかでもいいけど。 + ライブラリ側で、少しは融通効くようにすべきかな。 + やるなら、アドレスずらして取って来て、ユーザが見るデータは + そのずらした分戻してから見せるって感じ。変な説明だが。 + + うーん。今はとりあえず全てアライメント大丈夫な方向でやってみるか。 + + +2008-05-23 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/SpeThreads.cpp (SpeThreads::init): スレッドの生成 + 今まで作られてたスレッドは + + - spe_context_run を実行するだけのスレッド (spe_thread_run) + - 上のスレッドを呼び出し、終了を待つスレッド (frontend_thread_run) + + 2番目に何の意味があるのかということだが、 + SPE 毎にスレッドを立ち上げておいて、 + それぞれのSPEからのメールは、その担当するスレッドが見る、 + って構想で作っていました。だけど、今は mailbox の扱いは + Cell/CellTaskManagerImpl::mail_check で行っているため + わざわざ2番目のスレッドを作る必要がなくなりました。 + つーことで、frontend_thread_run ではなく、 + 最初から spe_thread_run を起動すればおkとなりました。 + + * Cell/SpeThreads.cpp (SpeThreads::get_mail): if 文排除 + 今までは + + if (spe_out_mbox_read(spe_ctx[speid], &ret, 1) < 0) { + return ret; + else + return -1; + + とやっていた。これは + + - データを読めたらそれ(ret)を返す + - データが無かったら -1 を返す + + ってことだったんだが、よくよく考えると、spe_out_mbox_read() は + データがなかった場合 ret の値を変えないので、最初に + + unsigned int ret = (unsigned int)-1; + + としておけば、最終的に if 文無しの + + spe_out_mbox_read(spe_ctx[speid], &ret, 1); + return ret; + + だけで済むわけだ。 + +2008-05-22 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * thinking: List DMA (2) + MFC List DMA read の場合は(少なくともPPEでcreate_taskする時は) + read size が決まっているので無問題。 + しかし、MFC List DMA write の場合。同じタスクでも + 違うサイズを出力するということはありえるので問題。 + 今までも、write の場合は task->run() の返す値が write size として + 使う事にしていた。List DMA write の場合は、おそらく + + - task->run() 内で write 用の List DMA 構造体を作って Scheduler に + 渡して、task->write() でやってもらう + + って感じ? でも(上の手法に限らず)、write のサイズが決まってないと + write 用バッファを生成しておく事ができないので + 書き込めない or あらかじめ多めに取っておくってことが必要になる。 + 後者は SPE には痛手(昔は強制16KB確保とかやってたな)なので微妙。 + 前者は論外だろう。 + + うーん、どうすっかな。Single DMA write の頃からこれは問題であって。 + 最悪、ユーザが「write のサイズが変動しないようなタスクにする」とか? + + * thinking: List DMA + + 構想としては以下のような考え。 + + class Task { + int cmd; + DataListDMA *rlist; + DataListDMA *wlist; + }; + + class DataListDMA { + int length; // リストの数 + unsigned int addr[128]; // アドレス + int size[128]; // そのアドレスから取得するデータのサイズ + }; + + 128 という数字は、一つのタスクが持てるリストの合計サイズを + 1KB (= 1024B) にしようってことで 4*128+4*128 = 1024 としました。 + ListDMA を使う流れとしては + + 1. Scheduler から cmd にそった Task を生成する + 2. Task のコンストラクタ(もしくは Task を生成する implement 内 )で + task->rlist, task->wlist を DMA read しておく (ここは通常のDMA) + 3. task->read() で MFC List DMA で List を読む + + DataListDMA->length に関しては、Task の中に入れるのも有りかと思う。 + その場合は、2 の DMA read で、わざわざ 1KB 全部読む必要は無くなる。 + + + * tag: v20080522 + - PPE 側のタスクも SPE と同じく、クラスオブジェクトとして登録 + - PPE、SPE 側の TaskManagerImpl を整理。見やすくなったと思われ + + こんなところかなー。 + テストプログラムは + + Game_project/student/master/gongo/hello + + にあります。DMA の例題まだだったぜHAHAHA + ここからは List DMA の処理を入れて行きたいと思います。 + + 現在の simple_render のバージョンは + PPE のタスクが関数ベースだった頃のなのでそのままでは動きません。 + List DMA ができるか、気晴らしに描き直すと思います。 + + * Task 定義について + PPE も C++ のクラスオブジェクトとしてタスクを定義するようにしました。 + ちゃんとした API を考えたら改めて書きます。 + + * メールチェックから次のタスクリスト生成までの流れの変更 + 今までの FifoTaskManagerImpl の mail_check では + + 1. mail_check + 1.1 check_task_finish + 1.1.1 notify_wait_taskQueue + 1.1.1.1 append_activeTask (依存満たしたタスクを) + 1.2 get_runTaskList + + と、全て mail_check の中で終わってたんですが、これを + + 1. mail_check + 1.1 check_task_finish + 1.1.1 notify_task_finish + 2. wakeup_waitTask (つまり append_activeTask) + 3. get_runTaskList + + というように分割しました。 + おかげで CellTaskManagerImpl の mail_check もすっきり。 + +2008-05-13 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/CellTaskManagerImpl.cpp (CellTaskManagerImpl::set_task): + // set_task って名前やめね? + + どの SPE に振るかって判定を少し変更。 + cur_anySpeid の宣言場所のコメントにもあるけど、 + これはインクリメントじゃなくて乱数の方が + より SPE_ANY っぽいのか。むしろ「仕事してない方に割り振る」ってのが + SPE_ANY の役目な気がするな。ウーム。。。 + +2008-05-05 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Cell/CellTaskManagerImpl.cpp (CellTaskManagerImpl::mail_check): + PPE には実行するタスクが一つも無い時の動作がおかしかった。 + 要するに、 + + PPE で実行するタスクは全て SPE で実行中のタスク待ち + + って時。。。よけいわからなくなったな。 + まあなんだ、今まで 必ずタスクが PPE and SPE にあったんだけど + PPE or SPE ってか、どっちか片方でしか動いてない状況だと + 終了判定というか、それがおかしかったっぽい。 + + Hello World でのタスクは + + 1. "Hello World!!" と表示するタスク (2.) を発行するタスク + 2. 表示するタスク + 3. 2 が全て終わったら実行される、最後のタスク(番兵的な + + この時、(2) が SPE だけで実行されてると、 + (2) の終了を待つ (3) の判定?というか、それがおかしい + + + もう眠くてわけわからん。 + 一応動いたんだけど、やはり描き直します。 + 気持ち悪いほどやっつけな書き方してるので。これはきもい。。。 + +2008-03-09 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * memo: pthread_cond_wait + この ChangeLog に書くものでもないが、まあメモ。 + + セマフォの P 動作は、基本的に + + --------------------- + pthread_mutex_lock(&sem->mutex); + + while(sem->value == 0) { // 資源が無い + // 条件付き変数に自分を登録して、ロックを解放して、他の + // プロセスが資源を解放してくれるのを待つ + pthread_cond_wait(&sem->cond,&sem->mutex); + } + // 自分の分の資源があったので、それを確保する */ + sem->value--; + // ロックを解放する + pthread_mutex_unlock(&sem->mutex); + ---------------------- + + こんな感じ。でコメントにもあるように、 + pthread_cond_wait では、wait の前に unlock する。 + これがよくわかってなくて、「while の外で lock してるのに + 「なんで他のプロセスが lock できるんだろう。」と。 + man 見ろよと思った。てか先生のページのコメントに書いてるよ! + + +2008-03-08 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * memo: mailbox read の blocking/non-blocking + spe_out_mbox_read は non-blocking API なので、 + これをぐるぐる回すと busy-wait になるので、 + 今の所 ppe 側の Scheduler がトップに戻る?時にメール確認する。 + で、spe_out_intr_mbox_read は blocking API 。 + spe_out_mbox_read との記述の違いは、予め + spe_event_handler_register で SPE_EVENT_OUT_INTR_MBOX を + 登録しておく。spe 側では、 + + spu_writech(SPU_WrOutMbox, data) + + じゃなくて + + spu_writech(SPU_WrOutIntrMbox, data) + + を使う必要がある。 + 両者の mailbox read の速度を調べてみたけど、そんなに違いは感じない。 + まあベンチマークの取り方がへぼいせいかもしれないけど。 + ってことで、こっちの intr の方がいいんじゃないかと思う。 + これと セマフォを組み合わせて mail の処理は簡単になると思う。 + セマフォの処理が重いって話もあるが、どうなんだろうね。 + + * Test/simple_render/task/create_span.cpp (half_triangle): fix + 画面外の span を描画しようとして落ちるので、それの修正。 + polygon->span の時点で外してるけど、span を外すより + Polygon の時点で修正するべきかな? + + * kernel/ppe/TaskManagerImpl.cpp (TaskManagerImpl::set_task): fix + 返す TaskList が、mainTaskList の最後尾になってた。 + ってことで、TaskList のトップである bufferManager->mainTaskList を。 + + * kernel/ppe/BufferManager.cpp (BufferManager::clear_taskList): fix + mainTaskList->length はクリアしてるのに、 + mainTaskList->next をクリアし忘れてた。 + だから空の TaskList が送られてたのか・・・ちくしょう! + +2008-03-07 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * bug-fix (Test/simple_render): y座標の移動方向 + (1) で、書き込む時に + + y = height - y + + としていた。千秋に聞いてみると、 + + 「ポリゴンの y を増やす(+)と、画面上に進むようにした」 + + だそうです。なるほどねー。ってことで(2)でもやったら上に進んだよ。 + + しかし、ゲーム的には上が + の方がわかりやすいかもしれんが、 + プログラミング的には、framebuffer ベースでやるので、 + 下にいくと y++ ってほうが作りやすいかなーと思いつつ。どっちがいいかね + + * bug (Test/simple_render): y座標の移動方向 + Viewer::run_draw で、従来の、SpanPack をそのまま描画する方法(1)と、 + SPE に渡すように、8分割したものとして描画する方法(2)で、 + それぞれの y に +0.5 とかしたら、移動する方向が違う。 + (1)では上、(2)では下へ行く。 + 送られてくる span には違いが見られず、 + x 方向や 回転は問題ないので、おそらく draw 時の y の計算のバグだろう。 + + 1: polygon.cpp Polygon::draw(SPANPACK); + 2: task/span_pack_draw.cpp run(); + + * Test/simple_render/spe/SpuDraw.cpp: ↓の続きの解決 + render_y &= ~7 + + これでおkでした。先生ありがとうございます。 + 今はマクロとして + + #define YTOP(y) (y & ~7) + + ってやってますわ。 + +2008-03-05 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * memo: MFC List DMA の element の最大値 + 「Cell Broadband Engine Architecture Version 1.02」 より + + P.55 + The maximum number of elements is 2048. + Each element describes a transfer of up to 16 KB. + + ってことらしいです。一度の転送での制限は普通のDMAと変わらず16KB。 + mfc_list_element_t は 2048 個まで設定できるってことか。 + テクスチャのロードで、分割しないなら MFC List DMA を使うことになるが、 + 2048 個もあれば充分? + + + * Test/simple_render/spe/SpuDraw.cpp: ↓の続き + と思ったけど、やっぱりずれるなあ。うーむ。 + とりあえず今は + + if (render_y < 0) { + int tmpy = render_y%8; + render_y -= tmpy + (tmpy != 0)*8; + } else { + render_y -= (render_y%8); + } + render_y += 1080/2; + + で落ち着くことに。うーむ。 + もっと良い計算を考えるよりは span の生成時で + いろいろやるほうがいいのかなー + +2008-03-04 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Test/simple_render/spe/SpuDraw.cpp: ↓の続き + よくよく考えてだな。。。マイナスが気になるなら + + if (render_y < 0) { + int tmpy = render_y%8; + render_y -= tmpy + (tmpy != 0)*8; + } else { + render_y -= (render_y%8); + } + render_y += 1080/2; + + じゃなくて + + render_y += 1080/2; + render_y -= (render_y%8); + + これでよくね?ってか元々そのための 1080/2 だった気が。。。 + + * Test/simple_render/spe/SpuDraw.cpp: render_y の計算の修正 + sp->span[0].y (SpanPack に格納されてる最初の Span の y 座標) から + この SpanPack が描画する範囲の一番上の y 座標を調べる。 + + どういうことかっていうと、例えば SpanPack に入ってる Span が持つ + y 座標が 1 ~ 8 の時 + + 1 ----- + -- + -------- + ---- + --------- + 8 -- + + '-' は描画していると思ってください。 + この場合は、y = 1 がこの SpanPack の一番上、基準となる 座標ってこと。 + framebuffer に書き込むとき、y = 1 から順々に書いて行きます。 + + で、sp->span[0].y ってのが、その基準となる y である保証が無いので、 + sp->span[i].y 、つまりどの y からでも、基準となる y を求める + 必要がある。その計算をミスってました。 + + 1 ////////// + <- なぜか書き込まれていない + ////////// + ////////// + + みたいに、歯抜けした部分があったので、いろいろ調べてみたら + この render_y がずれてるってことが判明しました。 + 今までは + + render_y = sp->span[0].y; + render_y += 1080/2; + render_y = (render_y/8)*8; + + ってことしてたんだけど、これだと sp->span[0].y が マイナスのとき + ずれることが判明。なので + + if (render_y < 0) { + int tmpy = render_y%8; + render_y -= tmpy + (tmpy != 0)*8; + } else { + render_y -= (render_y%8); + } + render_y += 1080/2; + + こうするとできました。。。が、直したい。 + もう少し奇麗に描けると思うんだけどなー。if 文ぐらいは外したい + +2008-03-03 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * memo: 最適化の結果 + ppe/spe ともに最適化なしの場合 + 263.444 FPS + + ppe だけ -O9 で最適化 + 317.425 FPS + + spe だけ -O9 で最適化 + 812.539 FPS + + ppe/spe ともに -O9 で最適化 + 1610.58 FPS (吹いた + + + 最初、ダブル最適化の画像を見た時の + あまりの早さにびびった。 + 逆に「こ、これはバグか!?」と思った + + +2008-02-28 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * kernel/ppe/BufferManager.cpp: remove_taskQueue_all() + taskQueue の create と free が釣り合って無くて、 + queue が足りなくなる -> extend_pool -> 足りなく(ry + ってのを繰り返してメモリ的なセグメンテーションフォルとが出て + なんでかなと思ったら、task->wait_me を消去してなかった。 + task->wait_i は notify(ry で削除されるんだけど、 + task->wait_me は、notify(ry に渡した後ほったらかしだった。 + ってことで、wait_me を全消しする関数を作りましたとさ。 + 気持ち速度が増した気がする。気ね。 + + +2008-02-17 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * Todo: 悩んでる所 + + + * fix: kernel/ppe/HTask.cpp + 今まで、manager->create_task で生成したタスクは + + - dependency の設定 + manager->set_task_depend(master, slave) // slave は master を待つ + + - 実行キューへの追加 + manager->spawn_task(master); + manager->spawn_task(slave); + + と、manager を介してやっていました。 + まあそれでもいいんだけど、特に dependency の所は + どっちがどっちを待つのかってのは、API見るだけじゃわからない。 + そこで、Task (HTask のこと) に、上二つに対応するような + + void set_depend(HTaskPtr) と void spawn(void) を追加しました。 + + - Usage + slave->set_depend(master); // slave は master を待つ + slave->spawn(); // slave をキューへ追加 + + 結局は、それぞれの関数の中では、上の set_task_depend とかを + 呼んでるんだけど、ユーザ側からするとこの方がわかりやすいと思います。 + +2008-02-16 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * tag: beta3 + ダブルバッファリングを追加し、まあなんとか動いてるんじゃない?って + ところまでですが、所詮 Fifo バージョンなので、 + そろそろ Cell を書き上げて、並列にちゃんと動いてるか確かめないとね + + * add: kernel/ppe/DmaBuffer.cpp + ダブルバッファリング用に作ったんだけど、 + せっかくなので、DMA は、このオブジェクト(が持つ二つの領域)でしか + 行えないようにする。ってのでどうでしょう。って話を先生としました。 + 並列処理だし、ダブルバッファリングがデフォでいいでしょう。 + というか、したくなければ swap_buffer とかしなければおk。 + + -Usage + DmaBuffer *buffer = manager->allocate(sizeof(SceneGraphPack)); + + 今までと違い、create_task の in_addr と out_addr には + DmaBuffer をいれてください。ユーザが自分で malloc/new したやつは + エラーを出すようにしてる(seg faultだけどね!) + 汚いソースだが、実際に使ってる様子は Test/simple_render の + viewer.cpp で使ってます。sgp_buff と pp_buff ってやつね。 + + もうすこしユーザに優しいAPIを作りたい・・・ + +2008-02-11 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: Test/simple_render + chiaki の DataPack を使った Cube の表示プログラム。 + 簡単に DataPack を TaskManager の scheduler (SpeManager) に渡して + 処理してコピーして、を繰り返してるだけなんだけど + まあ動いてる気がするのでいいんじゃないでしょうか。 + + +2008-02-10 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * tag: beta1 + この状況だと、できることよりもできないことを書くべき?違うか。 + + - task (親) 中で task (子) を生成することはできない + 正確には「生成はできる」だけど、その 子task が + 親task に依存している別の task を無視して動くので + ちゃんとした結果にならないと。 + 8日の Todo にも書いてあるけど、今の実装では + task が task を生成することを想定してない感じなので。 + 完全に spe 用にのみ狙いを絞った実装であって + OS って言えない実装であるからして、書き直すの? + 全ての関数を task しようとするとこうなる訳で、 + ある部分だけやるってのはまあできるんだろうけど、うーん。 + + - chiaki の simple_render が動かない + (追記) 解決しました + 単に read/write buffer のサイズが足りないだけだった。アホスwww + まあ辱めの為の下は残しておこう + + まだ cvs に commit してないけど、chiaki が書いた、 + DataPack 対応の simple_render に TasKManager を組み込んでみた。 + といっても、OSっぽく書いたんじゃなく、今は + update_sgp と create_pp だけを task 化してみた。 + でまあ動いてるような気はするけど、ものすっごい malloc 系の warning が。 + 時々長く動くよねみたいな感じになってしまっている。 + TaskManager が悪いのか、simple_render が悪いのか。 + この TaskManager、ある部分での malloc 系の問題に敏感だなあ。 + まあそうでなかったらバグの探しようも無いし、良いっちゃー良いんだが。 + + +2008-02-08 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * add: kernel/ppe/SymTable.cpp + 今まで func[] = {add, sum, ...} + とかやってかっこわるい言われまくってたので + 話し合いの通り Symbol Table みたいなものを作ることに + + // 疑似コードね + struct sym_table { + char *sym; // シンボル + void *address; // シンボルが示すアドレス + } sym_table[] = {{"Sum", &Sum} , {"Draw", &draw}}; + + int fd = get_fd("Sum"); + void *addr = get_address(fd); + + table には "Sum" と "Draw" っていう二つのシンボルが登録されている。 + 例えば、ユーザ(カーネル?)が "Sum" ってシンボルにアクセスしたい場合、 + まずは get_fd で "Sum" に対する、file descripter を返す。 + ユーザは、その fd に従って get_address を取得することが出来る。 + TaskManager 的な使い方をするなら + + // 俺は今、Draw 関数を使うタスクを生成したい + int fd = manager->open("Draw"); + manager->create_task(fd, size, in, out, func); + manager->open では get_fd と同じ使い方です。 + + まだ改良の余地ありそうですが、今は動いてるってことで。 + + + - 補足 + なぜ file descripter と表すか + + OS の昨日として、 fopen とかと同じ使い方もできるじゃない! + + + * Todo: task が task を生成する際の処理 + 今まで、 task が行う作業は、演算のみを行うような + 単純な実装に決め打ちされているわけです。 + しかし、OS なんかだと、タスク中から別のタスクを生成するとか + ありありだと思われる。てか今のテストプログラムでなった。 + + Test/Sum にあるプログラムで使われるタスク + + - init2 // 貧相な名前ですまない + 演算する数値とかバッファの初期化 + + - sum1 + ある範囲の整数 (i から i+16 とか) の総和 + + - sum2 + sum1 で求められた、複数の範囲の総和を一つにまとめる + (ex. 複数の sum1 が 1->16, 17->32, 33->48 の総和を計算し、 + sum2 で 上の3つの総和を計算する + 要は 1->48 の総和を分割するっていうプログラムね + + - finish + sum2 で求まった値を表示 + + この Sum というプログラム、というか OS と言おう。SumOS ね。 + SumOS は最初に TaskManager (所謂 kernel) を起動し、 + init を起動する。init では、予め決められたタスクである + init2 と finish の二つのタスクを create して登録する。 + init2 と finish には依存関係がある (init2 が終わったら finish) + init2 の中で、sum1 と sum2 というタスクが作られる。 + sum1 と sum2 にも依存関係はある (sum1 が終わったら sum2) + + 今の実装だと、タスクが終了して初めて次のタスクへ行く。 + まあ当たり前なんだけど、例えばそのタスクの中で + 新たにタスクが作られた場合、そのタスクが終了するまでは + 実行されなくなってしまう。 + でまあ、今は、manager->create_task される度に + manager->run とかして、無理やり起動してる訳さ。 + 何が無理矢理かっていうと、scheduler の役目をしている + SpeManager (これも名前変えないと) を2度呼び出してる訳。 + つまり、タスク中でタスクを作る度に、SpeManager オブジェクトを + new してるわけさ。いいのか?いや、動いてるけどね? + + ちなみに、Cell version だと spe が勝手に取っていってくれるから + 大丈夫かなと思いつつ、もし spe を1つしか使わない設定だったら微妙。 + + 要するに、タスク中でタスクが作られる場合の処理を考えないとね。 + +2008-02-07 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * memo: プログラミングの姿勢 + scheduler とか、task の管理をする部分は + kernel programing のつもりで、 + example とか、task に割り当てる処理を決めたりする部分は + user programing のつもりで。 + + それぞれ違った視点で見る必要がある + + * memo: OS というもの + OS 起動の流れ + + - PC の電源を入れる + - BIOS が立ち上がる (OpenFirmWare, EFI, BIOS) + - 起動デバイスをチェック (優先度とか種類とか) + - 起動デバイスから Boot loader を起動 + + BIOS によって、認識できるファイルシステムが違う(だっけ?) + + ファイルシステムのどこに Boot Loader があるか知っている + + grub, grub2, lilo, kboot などがある + - Boot Loader が kernel を起動 + + ネットワークブートの場合、TCP/IP や + ネットワークデバイス(イーサとか?)のドライバを持ってる必要がある + - kernel は、最初に scheduler を起動する + - scheduler の初期化 (init を呼ぶ?) + - init では、事前?に設定されているスクリプトとかを呼ぶ + + linux とかだと /etc/rc にあるやつを init が呼ぶ + - login form が起動 + + 補足 こっからユーザ + - login する + - shell を呼ぶ + + login shell かどうか確かめる + - ユーザに設定されてる起動スクリプト?を実行 + - 晴れてログイン + +2008-02-06 Wataru MIYAGUNI <gongo@cr.ie.u-ryukyu.ac.jp> + + * kernel/spe/*.cpp: new と placement new + 現在、spe kernel のタスクは、切り替わる毎に + new/delete を繰り返しています。今はこれでいいんだけど、 + 速度的にも、いずれは直さないといけないと思うわけで。 + で、予め allocate された領域を利用した placement new を使えば + new よりもそれなりに早くなるっぽい。 + 例題として、与えられた回数分 new/delete を繰り返すプログラムと、 + 同じ回数分、placement new したときの速度の比較 + + for (int i = 0; i < num; i++) { + + < task = new Task; + < task->init(i); + < task->printID(); + < delete task; + --- + > task = new(buff) Task; // buff = malloc(BUFF_SIZE); + > task->init(id); + > task->printID(id); + } + + placement new では、delete の必要は無い。 + その中で新たに allocate してるなら必要かもしれないが。 + 速度比較は以下。no_new が placement new で、ln_new が new/delete 。 + + % ./a.out 10 // 10 回 + no_new: time: 0.012135(msec) + ln_new: time: 0.003572(msec) + + % ./a.out 100 + no_new: time: 0.022453(msec) + ln_new: time: 0.018989(msec) + + % ./a.out 1000 + no_new: time: 0.115277(msec) + ln_new: time: 0.136335(msec) + + % ./a.out 10000 + no_new: time: 1.056156(msec) + ln_new: time: 1.322709(msec) + + % ./a.out 100000 + no_new: time: 10.622221(msec) + ln_new: time: 13.362414(msec) + + % ./a.out 1000000 + no_new: time: 109.436496(msec) + ln_new: time: 136.956872(msec) + + 10、100 回だと負けてるが、まあ無視しよう(ぇ + 回数が多くなるにつれて、ほんの少しだが no_new が勝ってる。 + どうなんだろうね。ちなみに printID を無くすと + + % ./a.out 1000000 + no_new: time: 0.008512(msec) + ln_new: time: 27.100296(msec) + + I/O に左右され過ぎ。まあそんなもんだろうけどさ。
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/TaskManager/Task.h Thu Jun 04 00:16:56 2009 +0900 @@ -0,0 +1,45 @@ +#ifndef INCLUDED_TASK +#define INCLUDED_TASK + +#ifndef INCLUDED_BASE_H_ +# include "base.h" +#endif + +#ifndef INCLUDED_TYPES +# include "types.h" +#endif + +#ifndef INCLUDED_LIST_DATA +# include "ListData.h" +#endif + +#define MAX_PARAMS 8 + +class Task { +public: // variables + + BASE_NEW_DELETE(Task); + + int command; // 4 byte + ListDataPtr inData; // 4 byte + ListDataPtr outData; // 4 byte + uint32 self; // 4 byte + + int param_size; // 4 byte + int param[MAX_PARAMS]; // 4*MAX_PARAMS byte + +public: // functions + int add_inData_t(unsigned int addr, int size); + int add_outData_t(unsigned int addr, int size); + int add_data(ListDataPtr list, unsigned int addr, int size); + int add_param(int param); + +#define add_inData(addr, size) \ + add_inData_t((unsigned int)(addr), (size)); +#define add_outData(addr, size) \ + add_outData_t((unsigned int)(addr), (size)); +}; + +typedef Task* TaskPtr; + +#endif