W.Deeの2006年3月の日記

kikyou.info»日記

誠に勝手ながら、kikyou.infoは2017年12月いっぱいをもって閉鎖します。ながらくありがとうございました。

最新月 : 2008年10月
2003年 [             3    4    5    6    7    8    9   10   11   12  ] 月
2004年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2005年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2006年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2007年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2008年 [   1    2    3    4    5    6    7    8    9   10   11   12  ] 月
2009年 [   1    2    3    4    5    6    7    8    9   10   11       ] 月
前月の日記  次月の日記

2006年3月25日

Boehm GC

Boehm GC の調査中にわかったことを 開発メモのBoehm GCの項 に書き記していますが、なかなかいやらしいですね。

とくにファイナライザ(デストラクタ)が呼ばれるタイミングや、そもそも呼ばれるか呼ばれないかすら信用できなくなるというのは痛いです。

デストラクタで重要な処理を行ってるのはなんとか回避するしかないですね。

自分の書いたプログラムはいったんぜんぶ見直す必要がありそうです。STL のアロケータも GC のにしないとなぁ。ざっと見回した範囲ではデストラクタではメモリの解放しているところがほとんどだから、それを気にしなくてもよくなるというのは気楽でいいです。

参照カウンタによる GC は AddRef と Release が釣り合わないと悲惨になりましたが、これはいくつかの「ルール」を守ればトラブルになることはまずありませんでした。Boehm GC の「ルール」はどうもそれよりはトラブルを起こしやすそうな気がします。

とりあえずしばらく使ってみます。というかもうすでに真 Risse の文字列管理部分とか書き始めていますが ... ええ、GC のおかげですごい「書きやすい」です。書きやすいけど「うっかり」GCの知らない領域にポインタを放置してしまったら ... とか考えると不安になります。

2006年3月23日

開発メモ

Trac に 開発メモ のページを作成しました。

2006年3月21日

Risse 着手

Risse の開発に着手します ...

いま Risa の上で動いているのは TJS2 を半ば無理矢理移植した中途半端なスクリプト言語なのですが、このまま Risa を構築して気づいたときには (現状のTJS2ベースのRisseに依存しすぎて) 手遅れだったという事態を防ぐため、新 Risse の実装に着手しようと思います。

Trac の方に wiki ページを作成しておきました → Risse の仕様や設計・実装方針などを考えるページ

Trac の wiki ページの編集は誰でもできるようにしておきましたので、気づいた点などあったらページの最後の方のコメント欄にでも書いておいてくださいませ。

2006年3月12日

吉里吉里3とRisa

前に吉里吉里3の内部名称をRisaにすると言いましたが、なんか開発中ずっとコアエンジンをRisaと呼んでいるうちに頭の中での名称が完全にRisaに切り替わってしまったような。

Risaをコアエンジンの名称ということにして、吉里吉里3をコアエンジンや周辺ツールも含めたソフトウェアパッケージの名称ということにしたほうがスマートかな。未定。

  • 2006-03-13 02:01 紅 要 : Risa いいですよね。なんかかわいい感じがしますw ただ、たまに Risa と Risse が混ざってしまうことが…w
  • 2006-03-14 00:03 W.Dee : 「なんかかわいい感じ」は半ば狙っています。混ざるのには慣れました。
吉里吉里3のSoundクラス

やっと(スクリプト制御で)音が鳴った!ということで見てもビジュアル的につまらないスクリーンショット

やっと(スクリプト制御で)音が鳴った!スクリーンショット

いまのところ再生できるサウンド形式としては PCM 系しか考えていないので、クラス名も単純にいまのところ Sound とすることにしています。

それにしてもなんか音質が悪い。再生サンプリングレートが低いような。OpenALの初期化に問題があるような気がするので見直します。

OpenAL 1.1 Core SDK 付属の OpenAL のソフトウェアミキサが waveOutOpen でデバイスを開くときに22.05KHzで開いていました。CVS trunkの最新のやつでは 44.1KHz で開くようになったので CVS 版をコンパイルして入れておきました(チェンジセット1466)。

  • 2006-03-12 06:34 HALO : ハートマークのアイコンが思いのほかステキでいいですね
  • 2006-03-12 13:38 W.Dee : うは。一応説明しておきますと、これはイベント配信の有効/無効を切り替えるボタンです。吉里吉里2でもそうですが、イベント駆動モデルで動いてるRisaはイベントポンプを停止すると実行が事実上 停止します。ハートのマークは、イベントを配信する心臓部である「ポンプ」としてのハート(心臓)のシンボルです。
  • 2006-03-13 08:56 霙 : まったく場違いですが・・・すいません、お聞きしたいことがあります。安定版をDLしても、「酔」とか「ョヘ」おかしな表記がみられ、解凍も正常にできませんなぜなのでしょう?
  • 2006-03-13 09:04 : まったく場違いですが・・・すいません、お聞きしたいことがあります。安定版をDLしても、「酔」とか「ョヘ」おかしな表記がみられ、解凍も正常にできませんなぜなのでしょう?
  • 2006-03-13 09:24 : 先ほどはお騒がせしました。どうやら自分の解凍ソフトに問題があったようです。おさわがせしました
  • 2006-03-13 11:19 W.Dee : お察しの通り、まったく場違いです。掲示板の方へどうぞ。→ http://kikyou.info/tvp/bbs/
  • 2006-03-14 16:08 SB : つまりRisaの生命ははユーザの思いのままということですね(ぇ
  • 2006-03-16 23:07 baku : オフにしたときのアイコンはどんなのでしょうねw
  • 2006-03-18 16:23 W.Dee : やっぱり逝っちゃった感じのアイコンですかね
  • 2006-03-18 21:51 紅 要 : ハートブレイクだったりはしないんですか?(w
  • 2006-03-21 00:34 SB : いや、あえて凄くリアルな潰れた心臓のアイコンを…

2006年3月10日

ZFS

Solaris の次世代ファイルシステムである ZFS がアツいようなので簡単に紹介。すでにOpenSolarisやSolaris Expressで使用可能のよう。

128bitアドレッシング
たぶん地球上では物理的に使い切れない量
トランザクション的操作
ディスク上のデータは必ず常に整合状態。fsck(1M)不要は当たり前、でjournaling filesystemではない
動的ブロックサイズ
処理負荷にあわせてブロックサイズを自動的に選択
管理が簡単
マウント・アンマウントも一つのコマンドで。/etc/vfstab を書き換える必要なし
プールモデル
プールという仮想的な領域からいくつでもファイルシステムを作成できます。パーティションとかスライスとかボリュームとか考えなくてよい。ディスクをプールに追加・削除することによりプールの容量を増減できる(ディスクの削除は開発中?)
ソフトウェアRAID
ストライピング、ミラー、RAID-5相当の機能を内蔵
適応型動的ストライピング
プールを構成するディスクの性能にあわせて自動的にストライピングを行って性能向上
チェックサム
ハードウェアや通信経路で発見されないようなデータ破損(silent corruption)をファイルシステムレベルで検出(メターデータもデータもすべてチェックサムで保護)
自己修復
RAID構成でデータ破損を発見した場合は、壊れたディスクを捨てるのではなく、正しいデータをディスクに書き戻そうと試みる
RAID-Z
RAID5のZFS版。ファイルシステムとの統合と可変ストライプ幅の採用によりRAID5の欠点(write-holeの存在、書き込みにおいてread-modify-writeが必須な点)を克服。double-parity 版の RAID6 相当の RAID-Z は開発中
透過的データ圧縮と暗号化
LZJB (LZSSの変形) による圧縮と何らかの暗号化アルゴリズムによる暗号化(暗号化機能はプロジェクトが始まったばかり)
スナップショットとクローン
瞬間的にある状態のファイルシステムのスナップショット(読み込み専用のコピー)やクローン(書き込みもできるコピー)を作成可能 (スナップショットやクローンの削除も瞬間みたい削除する際に解放されるブロックサイズに応じて時間がかかるが効率的)。特定のスナップショットやクローンをメインラインのファイルシステムに瞬間的に置き換えること(ロールバック)も可能。ファイルシステムレベルでバージョン管理ができそうですね。スナップショット数は2^64個までだからほぼ無限
オンラインバックアップとインクリメンタルバックアップ
スナップショットをとった上でのバックアップが可能、スナップショット間の差分を別のデバイスに転送することが可能(応用してリモートレプリケーションも可能)
すべてがオンライン
ディスクのプールへの追加、スナップショット等でシステムを停止する必要がない (といってもロールバックするにはいったんファイルシステムをアンマウントしないとならないみたいだが)
賢い先読み機能
任意の線形アクセスパターンを認識してデータを先読みする
I/Oパイプラインによる並列処理
I/Oパイプラインは著しく多重化しているようです。SMP環境でもI/Oをボトルネックにしない設計。I/Oに優先度を付加可能。すべてのディスクを束ねたI/Oバンド幅をすべてのアプリケーションで有効活用
POSIX互換
とりあえず普通に使えるみたい
適応型エンディアン
SPARCで使ってたディスクをx86でそのまま使うとか
オープンソース(CDDL)
Linuxへの移植もあり得るのか?

とまあ、発展途上ながらなんか機能がてんこ盛りみたいです。

ファイルシステムはメタデータやデータの区別無く、すべてが一つのツリー構造になっている様子。整合性の保持には「何か書き込みを行う場合は必ず元データを新しい場所にコピーしてから」(copy-on-write)で、1つ版のあがったファイルシステムのイメージを作成し、古い版を指している一番頭のポインタを新しい版を指すように変更する(ここがアトミック)という方法を行っているようです (ここのスライド(PDF)がわかりやすいです)。これはディスク上のフォーマットの説明(PDF)

この処理は何をやるにも毎回クローンを作ってから、最後にその新しいクローンを一つ前の古いクローンと置き換えているようなもので、スナップショットやクローンの機構はこれをそのまま使ってるようです。

どっかのファイルの1バイトを書き換える場合は、ファイルの元データブロックをコピーし、関係するツリーノードも親までさかのぼってコピーして更新して書き込むというI/Oが発生するようです。この意味ではRAID-Zでcopy-modify-writeが不要というのはちょっとまやかしっぽい?と思ったけど、RAID-5のように固定ブロックサイズを常にI/Oするのではなくて変更があれば変更のあった分だけのブロックサイズで書き込むのでより効率的ということなのでしょうか。

バルクな書き込みはともかく、既存ファイルの一部分だけを書き換えるようなある種の操作は従来型のファイルシステムに比べてかなり遅いんじゃないの?と思ったら、(僕が)心配していたような性能劣化はここを見る限り起きないようです(SolarisのUFSと比べて、ですが)。おおむね UFS より速いっぽいし、特定のデータアクセスパターンでは4~6倍速いらしいです。逆に特定のデータアクセスパターンではUFSに比べて数倍遅いですが、改善する余地があるとのこと。

まだ詳しくコードを追って調べた訳ではないので嘘書いてたらごめんなさい。

2006年3月5日

シングルトン管理(2)

前述のシングルトン管理ですが、モジュール化にも非常に役に立っています。

singleton_base からクラスを派生させるだけでシングルトンクラスを作ることができます。シングルトンインスタンスはとくに明示しなくても自動的に作成されます。

こういった機構がない場合は、何かクラスをつくったら、そのインスタンスを生成する部分を、「どこか別の場所」に記述する必要があります。これだと何か機能を追加するたびにその「どこか別の場所」も修正をしなくてはならず、やっかいです。

Risaで用いているシングルトン管理では、どのモジュールがリンクされているかに従って自動的に初期化を行ってくれます。シングルトンインスタンスを生成したくなければ単にそのモジュールをリンクしなければよく、新しく機能を追加する場合は単にモジュールを書いてリンクすれば良いと言うことになります。

たとえば Timer クラスを実装しているモジュールは、Timer クラスを Risse のグローバルオブジェクトに登録します。Timer クラスを登録するためのシングルトンクラス (レジストラと呼んでいます) は Risse スクリプトエンジンのシングルトンクラスに依存していて、Risse が起動してから Timer クラスが Risse に登録されることを確実にしています。

Timer クラスを登録しているレジストラをリンクしなければ、Risa で Timer クラスは使用不可になります。同様の方法でクラスを書けば自動的に登録が行われるため、どこか一カ所に「グローバルオブジェクトに各クラスを登録」のような処理を書く必要がありません。

レジストラのようなクラスは他のソースファイルから見える必要はないため、ヘッダファイルにレジストラのシングルトンクラスの定義を書く必要はありません。

この機構を応用してオンデマンドのDLLやDSOの呼び出しに利用できないかと考え中。

2006年3月4日

シングルトン管理

シングルトンというのは簡単に言ってインスタンスが1つだけのクラスです。

boost には details::pool::singleton_default というシングルトンを実現するクラスがありますが、Risaの用途にはあわないし、boost に入ろうとしてたここのシングルトンクラス はレビューではねられていたようでその後どうなったかわからないのでとりあえず自分で作ってみました。

結構便利です。

シングルトンは「インスタンスは一つ」という所だけを取り出せばグローバルに置いたオブジェクトと変わりがないのですが、C++で普通にグローバルにオブジェクトを置いてしまうと、オブジェクトの生成と消滅の順序を保証することが出来ません。シングルトンとは言っても、シングルトン同士の間には依存関係を持っているものがありますし、シングルトンを使う立場にあるオブジェクトは、当然どれかのシングルトンに依存しています。シングルトンに依存している他のオブジェクトが死ぬ前にシングルトンが死んだりすると厄介です。そこで、寿命管理が必要になります。

boost の details::pool::singleton_default は main 関数の前にすべてのインスタンスの生成が終了し、main 関数の後にすべてのシングルトンが消滅することを保証できるのですが、Risa の シングルトンオブジェクトの多くは wxWidgets に依存しており、wxWidgets は main 関数の直後に初期化されて main 関数が終わる前に消滅するのでこれは使えませんでした。あとコンストラクタで投げた例外を捕捉しようがないのがつらいです。

Risaで使っているシングルトンクラスは (実装は ここのSingleton.* )、

class SomeSingletonClass :
    public singleton_base<SomeSingletonClass>
{
public:
    // ctorとdtorはprotectedでよい publicにする
    SomeSingletonClass();//default ctor requied
    ~SomeSingletonClass();
public:
    void SomeMethod();
};

で SomeSingletonClass をシングルトンにすることが出来ます。SomeSingletonClass::instance() でインスタンスにアクセスすることが出来ます。

このシングルトンが他のシングルトンに依存している場合は

class SomeSingletonClass :
    public singleton_base<SomeSingletonClass>,
    depends_on<OtherSingletonClass>
{
 ...
};

と書くと、SomeSingletonClass は OtherSingletonClass に依存していることを表すことができ、SomeSingletonClass のインスタンスが生成される前に OtherSingletonClass のインスタンスが生成されることを保証できます (消滅の順序はその逆になる)。

depends_on はほかのクラス(なんでもよい) に継承させてもよくて、

class MultitonClass : depends_on <SomeSingletonClass>

と記述した場合は MultitonClass のインスタンスが生存している期間、そのシングルトンクラスが生存することを保証します。なにかクラスを作って、そのクラスのインスタンスが特定のシングルトンに依存していることを表す場合に便利です。

singleton_manager::init_all() は、未初期化のシングルトンインスタンスの生成をすべて終わらせます。この際に発生した例外などは捕捉することができます。

class SomeSingletonClass :
    public singleton_base<SomeSingletonClass>,
    manual_start<SomeSingletonClass>

として、manual_start を継承させると、singleton_manager::init_all では初期化されないシングルトンを定義することができます。この場合は一番最初に SomeSingletonClass::instance() (あるいは戻り値のない SomeSingletonClass::ensure()) が実行された時点でインスタンスが生成されます。あまり使われないくせに生成に時間のかかるシングルトンはこれにします。

singleton_manager::disconnect_all() は、すべてのシングルトンインスタンスを消滅させます。が、シングルトンクラスに依存しているオブジェクトがまだ存在していた場合などは、この時点でシングルトンインスタンスが消滅する保証はありません。

寿命管理には内部的に boost::shared_ptr を使っているので、相互依存 (相互参照) とかやってしまうとエラいことになりますし、最初にインスタンスが生成される際の、複数スレッドからの多重アクセスからの保護がなかったりします。汎用的に他のアプリケーションでも用いる場合はもっと考慮しなきゃいけないことが多いのでその際は書き直したいと思いますが、とりあえず Risa の用途では問題がないので、これでいいやと考えています。