view TaskManager/ChangeLog @ 625:60aa3f241b10

64bit mode worked on Mac OS X.
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 16 Nov 2009 10:59:55 +0900
parents 0decff4e867b
children 8843edf37c0e
line wrap: on
line source

2009-11-14 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

   やっぱり、TaskList の存在が許せない。あったとしても不定長でしょう。
   無駄なコピーが多すぎる。

2009-11-14 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    Scheduler / TaskManger / TaskManagerImpl の区別が不明

    Scheduler は SchedTask から直接見えないはずだが、SchedTask は、
    Scheduler は知っているが、TaskManager は知らない。これがかなりの
    混乱を生んでいる。

    SPU上では、TaskManager が存在しないのが原因らしいが、allcoate とかは、
    TaskManager が行うはず。なので、SPU上にもTaskManagerがある方が自然。

    SchedTask が自分自身で scheduling してしまっているので、Scheduler
    には、ほとんど仕事がない。なので、大半の処理を scheduler -> manager
    経由で行うことになる。

2009-11-14 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    要するに、SPE task 側から addOutData できればよい。
    でも、別に、PPE側から計算してもよいはずだけどね。
    そうすれば、renew task は取り外せる。

	SchedDefineTask1(DrawSpanEnd,draw_span_end);
    で、名前を指定させておいて、さらに、
        SchedExternTask(DrawSpanEnd);
        SchedRegisterTask(TASK_DRAW_SPAN_END, DrawSpanEnd);
    で、新しく名前を要求するのって、なんとかならんの? 読みづらいんだよ。
    DrawSpanEnd を、そのまま使ってもよさそうだけど?

    せっかく、renew task を外したのに、HD crash で失ってしまいました。

    add_param が順序を持っているのは見づらい。数字で指定する方が合理的。

2009-10-11 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    単純な、rbuf, wbuf + write return size の task のAPI
    List DMA の API
    投入 cpu 別の spawn method 
    Redering 時の内部からの DMA への直接アクセスへの禁止等など

    set_post で登録する関数も、task のrun関数と同じ型にした方が便利そう。

    SPU側でも配列(TaskList)ではなく、TaskQueue で管理すれば、
    renew task は簡単に実装できる。

    SchedTask の renew かそうでないかの区別は全部なくす。ex_init とかは、
    なくなるはず。その代わり TaskQueue で管理する。

    TaskList に inListData/outListData が入っているのは、やはりおかしい。
    もっとコンパクトであるべき。

    TaskList は、こまめに終了をPPE側へ知らせるのではなく、TaskListの
    書き換えで知らせる方が良い。

    SPUからPPUへ、create task 出来た方が良い。それはTaskList の書き出し
    で行なう。

2009-10-11 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    ようやっと直せました。inListData/outListData は別に転送しないで、
    一緒に転送してしまった方が良い。どうせ、いつも転送しているのだから。

    word_count が fifo の方が高速なのは、どうにかしてください。

    Renew Task の addInData は、メインメモリからDMAするので正しいらしい。
    直し方を間違えた。

    Task をmemcpyして、TaskList に入れているが、List DMA に直すべき。
    Simple Task を常に起動して、List DMA task は、その中で、Renew Task
    として起動するのが良いのでは? そうすれば、Task Load 自体を Task に
    出来る。

    Renew Task の実行順序が filo になっている。このあたり変なので、
    修正するべきでしょう。Renew用の TaskList を持てば良いんじゃないか?
    task->self の ad-hoc な使い方が泣ける。ひどすぎます。

2009-10-06 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    Task 内の create_task は、SchedTask に対してで、
    PPE 上では、Manager に対してだよね。だから、出来る
    ことがかなり異なる。これは、まずいだろ?

    特に、PPE task に明示的に manager を渡すってのは、
    とっても変。

    Renew Task の特別扱いが、いろいろ歪めているんだが、
    view.cc で使っているので落せない。

2009-10-05 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    TaskQueue のfree list の管理はシステムで一つであるべき。
    TaskQueue は double linked list が当然らしい。

2009-10-02 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    DrawSpan で、~DrawSpan() で、allocate したデータを DMA_WAIT
    して、free しているが、これは、抽象化違反。Task で明示的に
    DMAするのは禁止。Task 内で、add_outData 出来れば良い。

    renew が正しいような気がするが...

    Task 内で大域変数は使えない。なので、smanager からallocateする
    必要がある。Task の解放のタイミングではなくて、パイプラインの
    タイミングでDMA waitとfreeを行なう必要がある。DrawSpan の場合は、
    add_outData で良いが、内部で allocate/free は行なう必要がある。
    put_segement がパイプライン動作するべきなのか?

    固定のDMA tagが邪魔。

    DrawSpan は全般的にダメだな〜

    でも、その変更は大きいので、とりあえず動くようにしたい。

    memset 0 は、7.7.3 SL1 Data Cache Range Set to Zero コマンド
    つかうべき。SPE側でやっても良い。でも、本来は全面埋まるのが
    普通なのでどうでも良いけど。

2009-08-06 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    で、MemList/MemHash が TaskManager 側に移ったので、
    これで、code の management を書くことが出来る。
    そうすれば、SPEのメモリの限界をほんと気にする必要がなくなるはず。

    その前に、get_segment の例題を直さないと。

    DrawSpanRnew/reboot は使ってないらしい。

    Tree は、配列にしないでlinkをSPE側からたどるようになっている。
    それは良いのだが、Task 側で dma_wait するような実装は望ましくない。
    この部分も書き直す必要がある。list 構造の SPE上の Iterator を
    実装すれば良い。

    memory 関係のコードが scheduler の下にあるのは面白くない。

    Scheduler で実装(__scheduler)に移譲している部分は、headerに
    移した方が良い。

2009-08-06 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    うーん、get_segemnt で、dma_wait のtagをなんとかする
    必要があるらしい。get_tag() でなんとかなるけど、
    他のtag との関係があるかな。

    完全に見えなくするべきでしょうけど... 今はいい。

2009-08-01 Shinji KONO <kono@ie.u-ryukyu.ac.jp>

    MemList は動いたので、今度は TileHash を TaskManager 側に移動する
    必要がある。

    その後、コードのLRUを書けば、Cerium は一通り出来上がり。

    TaskManager と Scheduler の関係が一貫してない。複雑すぎる。


2009-07-24 Kaito TAGANO <tkaito@cr.ie.u-ryukyu.ac.jp>

	長さ別の freeList と単一の HashTable で管理する
	TileList を廃止
	class MemorySegment {
		MemorySegment *next;
		MemorySegment *prev;
		uint64  size;
		uint64  address;
		uint64  dummy;
		// uint32  data[0];
	}

	class MemList {
	 	MemorySegment* first;
		MemorySegment* last;

		MemList* createMemList(uint32 size, uint32 count);
		void addFirst(MemorySegment* e);
		void addLast(MemorySegment* e);
		MemorySegment* getFirst();
		MemorySegment* getLast();
		boolean remove(MemorySegment* e);
		void moveToFirst(MemorySegment* e); // or use();
	}
	
	サイズ毎に freelist と activelist を持って、これを malloc free
	として使う。
	これのテストルーチンを書き終わったら、Tapestry をこれで書き直す
	LRU は使うたびに以下を呼び出す

		void use(MemorySegment* e, MemList* active) {
			active.remove(e);
			active.addFirst(e);
		}
	

2009-07-15 Yusuke KOBAYASHI <koba@cr.ie.u-ryukyu.ac.jp>

	PPU からMainMemory にResource を Access する API
	長さ別の freeList と単一の HashTable で管理する

	読みだしAPI。set_rgb に相当。
	uint32 segment_id = smanager->get_segment(memaddr addr, *MemList m)
		id は hash値に相当。
	addr で指定された PPU の Address が Hash にあるかどうか調べる。
	    無ければ dma_load する。そして指定された id を返す。

	確保 API。set_rgb に相当。読み出さないで、hash entry だけ確保する。put しかしない部分用
	uint32 segment_id = smanager->get_null_segment(memaddr addr, *MemList m)
		id は hash値に相当。

	書き出しAPI、読みだしていること前提。	
	smanager->put_segment(wait_id);

	MemorySegment* smanager->wait_segment(uint32 segment_id)
	    id で指定された PPU の segment の copy の Address を返す。
	    必要があれば dma_wait を行う。書き出しも待ち合わせる。
	
2009-06-8  Shinji KONO  <kono@ie.u-ryukyu.ac.jp>

	SchedTask/SchedTaskImpl の分離はあんまり意味がなかった。
	SchedTaskBase が既にあるし。
	とりあえず、__list とかは、private にしただけ。
	ScheTaskImple を作っても、継承してprivateにすると、
	warning は出るが、 User Task space の名前空間は結局汚れてしまう。

	delegate するべきだと思うが、SchedTaskBase でないと、
	動かないらしい。それだと、indirect が増えるので、ちょっといや。

2009-06-4  Shinji KONO  <kono@ie.u-ryukyu.ac.jp>

	set_symbol は、もういらないよね?

	list dma 中心の実装にして、もっと細かく read/exec/write
	した方が良いかも。

	post で、PPE task を渡せると良い。address は parameterとして送る

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 に左右され過ぎ。まあそんなもんだろうけどさ。