2011年12月10日

環境依存部分

Linux で動作を確認したらパス周りでうまくいかないところがあった。
File.separatorChar で区切りを分けることはしていたけど、ドライブレター周りの処理が不完全だった。
対処はしたけれど、もう少し処理を見直した方が良さそう。

また、文字表示をしようとした時もフォントの指定に不備があったようで思うように出なかった。
吉里吉里2でデフォルトになっているMS Pゴシックを探して見付からなかったら、Locale.JAPAN で列挙したリストの一番上のものを指定していたけど、うまく出なかった。
Font.MONOSPACED を指定するようにしてもうまくでず。
仕方ないので、フォント候補を列挙してその中のものを探すようにした。
IPA や VL、さざなみを入れたので、Linux だとだいたい見付かると思うが……
Mac OSX はよくわからないので、ヒラギノ角ゴとヒラギノ丸ゴを入れておいた。
日本語を表示できるフォントをうまく指定できる方法があるといいんだけど。
swing のウィンドウタイトルとか日本語表示しているので、その辺りのプロパティから引っ張ってくるのがいいんだろうか。
まあ、選択するフォントリストを明示するのが確実と言えば確実だけど、汎用的な方法があるのならその方がいいし。
ちなみにリスト全て見付からない時は Font.MONOSPACED で指定されたものを使う。
確実なのはIPAフォントを内蔵してしまうことだけど、ちょっと大きいのでその辺りは選択肢として別に用意しておいた方が良さそう。
フォントを含めておいてデフォルトはそれにする等。

まだ実装はしていないけれど、動画周りもネック。
Android は別にいいとしてそれ以外の Java環境でどうするか。
検索してもあまりいいものは見付からず。
Java でデコーダー書くのはさすがに厳しいと思うので、ここはJNIを使ってしまうかもしれない。
作りかけの動画再生エンジンを使って、マルチプラットフォームのデコーダー書くつもり。

Android とその他ではグラフィック周りやWindow周りでいろいろと依存部分があるけど、とりあえず割愛。
そこは分離して作ってはいる。

投稿者 Takenori : 16:52 | コメント (0) | トラックバック

2011年12月13日

プラグイン

ソースコードを加えてコンパイルしてしまえば、プラグイン機構はなくてもいいと言えばいいんだけど、あった方が良いことも確か。PC なら、jar ファイルからクラスを読み込んで動的にロード出来るので、その辺りのハンドリングを決めておけば実現することは難しくない。
リフレクションによって、Java のクラスを TJS2 のクラスに登録してしまえるので、クラスの追加は楽に行える。

問題は、Android 。
以前、LGPL の ライブラリを使った時は、動的読み込みにするために Android のサービスによる RPC を使った。
計測はしていないが、RPC なのでオーバーヘッドが気になる。
で、以前少し TJS2 を Delvik バイトコードにして動かすことについて調べていた時に、ClassLoader に Delvik バイトコード食わせてクラスをロード出来るのを思い出し、検索してみたら普通に dex ファイルからクラスを読めるようだ。
以下参考。

Custom Class Loading in Dalvik(翻訳)
アプリケーションパッケージ(.apk)中のクラスを列挙する
アプリケーションパッケージ(.apk)中のクラスを列挙してロードする

これが出来るのなら Android でもプラグインを作ることが出来るな。
吉里吉里Javaとは関係ないけど、サービスを使っていたものはこっちに切り替えようかなと思った。

リフレクションによる呼び出しのオーバーヘッドは、TJS2 の関数呼び出しから見た場合、それほど大きくない。
TJS2 の関数呼び出しとループ処理(TJS2から複数回呼び出しのためのループ)を合わせて計測した場合、リフレクションにしたことによるオーバーヘッドは10%強程度。
周辺部にある程度呼び出しコストがあるためか、リフレクションのオーバーヘッドは意外と少ない。
集中的に呼び出した場合でこれくらいなので、実使用で組み込み関数呼び出しばかりするような状況でなければ、それほど気にすることはないようだ。
ただ、TJS2 の組み込みクラスはリフレクションではなく、メソッドごとにクラスが作られるスタイルで実装したが(オリジナルのC++のTJSと同じような形)。
リフレクションと比べた場合のデメリットは、クラスが増大するためかバイナリサイズは大きくなることと、ちょっと記述が面倒なこと。
オリジナルはマクロで軽減しているが、Java はマクロがないのでちまちま書く必要がある。
無名インナークラスがあるおかげで、C++ で書くのに比べたらかなりマシだけど。
リフレクションなら、簡略化しようとしたら登録関数一発で Java のクラスを TJS2 のクラスに出来るけど。

初期リリースにはプラグイン機能を入れないつもりでいるけど、入れられることがわかったので良かった。

投稿者 Takenori : 18:47 | コメント (0) | トラックバック

2011年12月20日

KAG3 のコンパイル時間とバイトコード

オリジナル : KAG3 のスクリプト読み込みに 90ms、KAGMainWindow のコンストラクタに 55ms。
吉里吉里Java : KAG3 のスクリプト読み込みに 335ms、KAGMainWindow のコンストラクタに 633ms。
Java 版遅い。
コンパイル時間は最初計測した時、600ms強だったんだけど、System.gc をコンパイルごとに呼ぶのを止めたら倍くらいに早くなった。
強制的に GC を呼ぶのはよくタイミングを考えた方がいいか。

コンパイルが遅いのはある程度予想していた。
オリジナルは文字列のポインタを直接読んで字句抽出しているところを Java版では文字アクセスクラス経由で実装しているのでそこは少し遅いはず。
Java だと配列にしても範囲チェックが入ってしまうのもある。
ここはもっとダイレクトにアクセスするようにした方がいいかもしれないが、ネイティブに比べて 3.7倍程度で済んでいるので、それほどコンパイル遅いと言うことはない気もする。
後、構文解析器がオリジナルは bison なのに比べて。Java 版は再帰下降法で手書きされている。
昔に読んだ yacc の本では、yacc は手書きよりも少し効率が劣ると書いていたけど、今どうなんだろう?
flex は、手書きよりも効率の良いコードを出力するとその時でも書いてた。
ただ、私が書いたパーサーが早いかどうかはわからないが。

TJS2 のベンチマークでは、使用している PC に比べて Android は80倍くらい遅かったので、それをそのまま当てはめて考えると、コンパイルに 26秒もかかってしまう。
これはちょっと致命的。
ただ、KAG3 のコンパイルは別に実行時にやる必要はないので、バイトコードを入出力できるようにすれば、ここは極めて高速に処理できるようになるはず。

で、KAGMainWindow のコンストラクタ。
何でこんなに遅いのだろうか。
何が遅いのか調べて最適化しないまずい。
Window周りの設定は、Android だとほとんど無視というか、出来ないのでそこがネックになっているのなら、Android はそれほど遅くないことになりそうだけど、調べてみないことには何とも言えないな。
コンパイル時間はバイトコード使うことで大きく短縮できるが、KAGMainWindow のコンストラクタは地道に最適化しないとどうにもならない。

投稿者 Takenori : 18:42 | コメント (0) | トラックバック

2011年12月23日

クラスのロードが遅い

KAGMainWindow のコンストラクタが遅い問題でいろいろと速度を計測していると、どうもクラスのロードが遅い様子。
ネイティブのWindow クラス ( JFrame を継承したクラス ) のロード自体に 120ms 程度かかっていたので、JFrame を new してみると最初の1回目だけ遅い。
そこで KAGMainWindow を2回作ってみると、2回目のコンストラクタは 162ms になった ( 2回同じところを通ることで特定部分についてはJIT等の影響があるかもしれない )。
オリジナルの 55ms に比べると3倍程度かかっている。

TJS2 のコンパイル時間と合わせて推測すると、純粋に処理時間としてはネイティブに比べて、Java は3倍程度なのだろうか?
ベンチマークで速度が出るのは、JIT によってネイティブコードになることで、30%程度の速度差で済んでいるのかもしれない。

クラスのロードが遅いとすると、Android では Window は違った傾向を示すと言うか、このタイミングで遅くなることはないはず。
Android は、最初に生成された Window を Activity と関連づけるようにする予定なので、KAGMainWindow のコンストラクタが呼ばれる時点では、既に Activity はロードされている。
だから、ここでクラスロードに時間がかかる可能性は低い。
ただ、クラスロード時間がないと思われる 162ms を基準にしたとしても、この時間の 80 倍時間がかかると 13秒もここでかかってしまう ( Activity 以外のクラスロードはあるのでもっと遅いかもしれない )。
スプラッシュを何個か出せばごまかせなくはないかもしれないけど、これは辛い。
Android の場合、Window 周りの処理やメソッドの多くは無効というか設定できなくなるので、この辺りで少しは高速化されるかもしれないが……
地道に最適化していくしかないか。
そのままでも動くけど遅い、KAG3 にある程度手を入れてプリプロセッサで切り分けて、Android 版は軽量化を図るとかになってしまうかもしれないな。
Android からすると要らない(無効な)処理が、KAG3 には入っているのでそれらを削れるだけでもだいぶ違うはず。
どうにもならなくなったら、Dalvik バイトコードへの AOT コンパイルか。

投稿者 Takenori : 19:11 | コメント (0) | トラックバック

2011年12月28日

direct threaded code

VM を素直に実装すると、switch-case で命令を振り分けて対応する処理をするような作りになる。
これを switch-case で振り分けるのではなく、命令(だいたい単なる整数)に処理が書かれた先のアドレスを埋め込み、そこへ直接ジャンプすることで、switch-case をなくす。
さらに、そこから次の命令に飛ぶ時、その命令の処理の末尾で直接次の命令の処理先へジャンプすることで、一度命令を振り分けるところに戻る処理を省ける。
この辺り threaded code や direct threaded code などと言うテクニックのようだ。
説明は、YARV Maniacs 【第 3 回】 命令ディスパッチの高速化を読むとわかりやすい。
後、Context threading と言うのもあるよう。

なるほど、そう言うテクニックがあるのかと思ったけど、Java では書けない。
C言語でも厳しい。
gcc だとラベルのアドレスを拾って、そこへ飛べる拡張があるようで、gcc なら C言語で書けるようだ。

高速化のために調べたものの、吉里吉里Java では使えそうにないことがわかった。
Java や Dalvik バイトコードを出力できるようにすれば大丈夫だけど、その場合、switch-case とかそう言う話ではなく、TJS2 のバイトコードではなくなってしまって、JVM や Dalvik VM で命令部分が処理されることになるんだけど。

その他参考リンク

SquirrelFish Extreme 高速化の話
direct threaded code
direct threadingでフィボナッチ計算
VM の高速化のために Context Threading を学ぶ - Scheme VM を書く

投稿者 Takenori : 21:13 | コメント (0) | トラックバック

2012年01月01日

Android での起動時間

Android で KAG3 がある程度動くようになったので、速度を計測した。

Xperia arc (Android 2.3.3) で測った結果

KAG3のコンパイルに 14秒。
KAGMainWindow のコンストラクタに 2秒。
吉里吉里の初期化全体で 19秒。

コンパイルとコンストラクタ以外の部分で 3秒かかっている計算になる。
TJS2 のバイトコード読み込みに対応して、事前コンパイルしておけば、起動は 5秒程度まで縮まるだろうか?
コンパイル時間にはトップレベルスクリプトの実行時間も入っているので、14秒が丸々なくなることはないだろうと思われるが。
2.2 未満の場合もっと時間がかかりはず。

まあ、何にしても 5秒程度であれば許容範囲だろう。
スプラッシュを出せばそれほど気にならない範囲ではある。
Dalvik バイトコードを出力しなくても、TJS2 の事前コンパイルのみでも何とかなりそうなことはわかった。

ただ、開発中は何度も再起動するので、起動時間の短縮は開発中にいろいろとがんばるはず。

投稿者 Takenori : 23:53 | コメント (0) | トラックバック

2012年01月03日

OutOfMemory対策

Android で画像を読み込んだり、生成した時によく発生する OutOfMemoryError 。
本当にメモリが足りない時はどうしようもないが、解放されずに残っているオブジェクト等の影響で確保に失敗することがある。
このような状況を避けるためには、単純に以下のようにして1度目は OutOfMemoryError 例外をキャッチして、GC を実行し、再度確保を試みるようにすれば、成功することが多い。
当然、自身で多くのメモリを使用している場合はどうしようもない。

try {
 mImage = Bitmap.createBitmap( w, h, Bitmap.Config.ARGB_8888 );
} catch( OutOfMemoryError e ) {
 java.lang.System.gc();
 // 2回目は try-catch せず、2回目も失敗した時は諦める
 mImage = Bitmap.createBitmap( w, h, Bitmap.Config.ARGB_8888 );
}

後、Bitmap は不要になった時、bitmap.recycle(); して、null を代入しておいた方が良い。
recycle() は、ネイティブオブジェクト(ピクセルデータ) との関連を強制的に断ち切る。
Bitmap オブジェクトが他で参照されていても GC が発生したら、ピクセルデータは解放される。
当然、再使用しようとしたらエラーになる ( isRecycled で使えなくなっているかどうか確認できる )。

吉里吉里Java だと、上記の GC を試してみる前に、System.doCompact 処理を実行して、内部のキャッシュを参照解除 して、GC で解放されるようにする。
これで出来るだけメモリを空けて Bitmap の読み込みや生成が成功するようにする。
このような処理で OutOfMemoryError で落ちる確率は減るが、GC が走ると数百msec程度処理が止まる。
落ちるよりはマシだが、カクつく原因にはなる。

再読込等で生成しやすい画像データは、読み込んだ後 recycle() をあらかじめ実行して置いて、必要時にはisRecycled でまだ使えるかチェックして、使えなくなっている時は再度読み込んで必要な処理を行うことで、より OutOfMemoryError に陥る可能性を減らせるようだが、頻繁に再読み込みが発生すると困るので、このような処理は行っていない。
メモリ的に厳しいようであれば、このような処理も検討する。
実際問題として recycle() した後どの程度解放されてしまうのかによっては、普通に使用しても問題ないかも知れないが、ドキュメントを見る限り次に GC が実行されたら消えてしまうように思える。
2回目もメモリ確保に失敗した時は、ロードしやすいデータは recycle() してしまい、OutOfMemoryError で落ちる確率をより減らすような処理があった方がいいかもしれない。
ただ、その場合は今表示されているデータが解放されてしまい、ロード地獄に陥るかも知れないが。

投稿者 Takenori : 22:05 | コメント (0) | トラックバック

InterCodeContext を分離

オリジナルの tTJSInterCodeContext と同様に、吉里吉里Java も InterCodeContext でコード生成と実行時のオブジェクト実体 ( VM ) の両方を兼ねている構造だったが、TJS2 バイトコードを読み書きしやすいようにするためと、見通しを改善するために、InterCodeGenerator と InterCodeObject に分離した ( TJS2の disassmbler も一体化していたんだけど、こっちも分離済み )。
分離に伴い、InterCodeGenerator から InterCodeObject を生成する時に少しのコピーとオブジェクトの置き換えが発生するために、少しコンパイル速度は低下しているはず。
ただ、PC 上では特に速度低下はみられなかった。
全体に占める割合からすると微々たる処理なのかもしれない。
後、ScriptBlock と言うのがあって、これはだいたいtjsファイルと1対1で対応するのだが、これもコンパイル処理と実行時に InterCodeObject を束ねる機能を兼ね備えている。
別に一体になっていてもいいが、見通しと実行時のメモリ使用量の削減 ( 微々たるものだが ) のために分離を考えている。

これらを分離すれば、Compiler + InterCodeGenerator → ScriptBlock + InterCodeObject と言う通常の TJS2 コンパイル動作と、バイトコードローダーから ScriptBlock + InterCodeObject を生成する処理を綺麗に分けられる。
生成元がTJS2コンパイラかTJS2バイトコードローダーかに関わらず、同じように実行時のオブジェクトを生成出来る形になる。
また、InterCodeGenerator は、CodeGenerator インターフェイスを継承するクラスにしようと思っている。
実際のバイトコードを生成するクラスを抽象化することで、TJS2 バイトコードのみでなく、Dalvik バイトコード等を生成しやすい構造にして行く狙い。
実際に Dalvik バイトコード を生成するクラスを作るかどうかはともかくとして。

このような処理によってコンパイル性能は若干低下するかも知れないが、リリース時はバイトコードを直接読む形になるであろう事から、それほど問題はないと考えている。
PC の場合は、あまり速度低下自体見られないようだし問題はないと思う。

投稿者 Takenori : 22:56 | コメント (0) | トラックバック

2012年01月13日

バイトコードファイル読み込み

現在以下仕様で実装している。
Script.execStorage Script.evalStorage で指定されたファイルの拡張子が tjb ならバイナリバイトコードファイルとみなして読み込む。
拡張子 tjs の時は、同名で拡張子が tjb のファイルがあるかどうか見て、あったら tjb ファイルをバイナリバイトコードファイルとして読み込む。
上記、条件に合わない時は tjs ファイルをスクリプトとして読み込む。

この仕様について、以下のような提言を受けた。
----
いまどきの実装なら拡張子依存ではなく、マジックコード読んで処理するほうが良い。
a. 内部的なロード処理ではマジックコードでどちらでも読めるようにする
b. ファイル名のマッピングを差し替えれる機構をプラグ的に準備(拡張子を差し替えて優先ロード、とか) はそれぞれ別にする。
勝手に指定してないファイルが読まれる可能性がある仕様は個人的には好ましくない。
----

この部分の仕様は開発をどのように進めるかが仕様決定の主因であり、それを想定して仕様を決めた。
拡張子依存ではなく、マジックコードで~と言うのは確かに一理ある。
一応、バイトコードかどうかの判定は、埋め込まれた FourCC 等によって判別はしているが、一致しなかった場合にスクリプトとみなしてコンパイルを試みる等の動作はしていない。

スクリプトをそのままコンパイルして、コンパイルしたバイナリをデータフォルダに突っ込めば、勝手にそっち読んで動いてくれる。
バイナリとスクリプトを見分けやすい。
バイナリファイル消せば、スクリプト読んで動いてくれる。
これらの動作を元に仕様を決めた。

提案のあった内容は、どのような使い方を想定したものなのだろうか?
拡張子ではなく、マジックコードで判定するのはいいとして、引き渡すファイルの拡張子は tjs にするんだろうか?
だとしたら、スクリプトの tjs ファイルと、バイトコードになった tjs ファイルが混在して混乱の元だと思うけど、そこは気にしないのだろうか?
もしくは、引き渡すファイルの拡張子を元々 tjb 等として、バイトコード前提のスクリプトを別に準備するのだろうか?
バイトコード前提のスクリプトを別に準備するとなると、常にコンパイルしなければならなくなるが……
毎回コンパイルを避けるとなると、TJS で自前で tjb ファイルがあったらそっちを読む等の判定処理を書いて、それを使う前提なのだろうか?
TJS で自前で書とある程度スクリプトに修正を入れることになるが、上述した中ではこの使い方が一番現実的か。

拡張子ではなく中身で判別するべき。
指定していないファイルが読まれるのは好ましくない。
と言う二点によって、提案されたもので実際の用法を考慮に入れたものではない可能性もある。


再度仕様を考える。
1. 指定されたファイルの中身でファイルの種類を判別し、スクリプトかバイトコードとして読み込む。
2. バイトコードでない場合は、tjb ファイルを探すが、探すかどうかはオプションによって切り替え可能とする。
このような形が妥当か?
ただ、この場合 TJS2 以外のスクリプトを許容するようになった時に破綻するが、その時は拡張子で判定を再び復活させるか、別のメソッドにすることになる。
まあ、そうなることはなかなかなさそうだけど。
後、推奨する使用法は最初に書いた拡張子を使ってくださいと言うことになるかな。

----
追記
開発時はスクリプト、リリース時にバイナリ化すると言う運用なので、ファイル名は同名で問題ないと言うことのようだ。
運用上混在しない形だったのか。
ただ、Android だとコンパイル時間がある程度かかるので、事前コンパイルしておいた方が良いと言うことについては、フォルダを分けで make 等使い毎回コンパイルするようにして回避すると言う案のよう。

投稿者 Takenori : 18:15 | コメント (0) | トラックバック

2012年01月24日

サスペンド / リジューム

Android での起動の高速化のためにあれこれ考えているが、起動した直後の状態を直接構築出来れば、それが一番速い。
startup.tjs を実行した直後の状態が起動直後の状態なので、その時の状態を保存して、その状態を復元することが出来れば理屈上は最も速く起動が完了するはず。
ただ、環境によって起動状態を変更する必要が生じた時に対応出来ない。
依存しそうなものは、画面解像度とSDのパス辺りかな。
依存部分だけ保存しない作りにすればいいが、後々維持管理が大変になる可能性が高い。
startup.tjs 実行直後の状態を保存して~と言うのは避けた方が良さそう。

いつでも状態を保存して復元できると言うのは、Android だとあるとかなり助かる機能なのは間違いない。
他のアプリが立ち上がってバックグラウンドに行って、気付いたら死んでることがあるので、オートセーブは必要になるだろうし、スマートフォンの用法を考えるといつでも中断/再開が出来るとありがたい。

理屈上はシリアライズに対応すればサスペンド / リジュームに対応出来る。
シリアライズの問題点として、オブジェクトを保持している時はいいとして、参照しているだけの場合どのようにしてその状態を保存し復元するのかと言うものがあり、これは2パスで保存するしかないと思っていた。
少し調べて見ると、これは 時空を越えるオブジェクト の一番下の方法によって解決出来ることがわかった。
後、問題となるのはレイヤー画像の保存。
読み込んだままの画像はファイル名を覚えておけばいいが、変更が加えられている場合は画像そのものを保存しなければならない。
メッセージレイヤーは確実に画像を保存することになると思われる。
他には、TJS2 で記述された各種オブジェクトのバイトコードの保存と読み込み、全クラスでのシリアライズ処理のサポート。
保存と復元に時間がかかりすぎるとあっても無意味。

サスペンド / リジューム出来るメリットは大きいが、その分実装とテストも大変。
対応するかどうか悩むところ。

投稿者 Takenori : 17:39 | コメント (0) | トラックバック

2012年01月28日

Layer.affine 系

吉里吉里2の Layer.affine 系のメソッドは、matrix 指定と頂点指定のどちらかを選べる。
吉里吉里2では、matrix で指定された場合、頂点に変換して、頂点を使って描画する。

吉里吉里Java の場合は、常に matrix を指定して描画する。
そのため頂点指定の場合は、頂点から matrix を割り出す必要がある。
matrix から頂点を求めるのは簡単 (普通の行列演算) だけど、逆は手間がかかる。
最初は幾何学的に求められるかと思ったが、いろいろ検討するも難しい。
と言うことで方程式を解くことに。
吉里吉里2では3つの頂点が与えられるので、2つの連立三元一次方程式を作ることができる。
これは matrix から頂点を求める式そのまま。

連立三元一次方程式1
x0 = (-0.5)*a + (-0.5)*c + tx
x1 = (width-0.5)*a + (-0.5)*c + tx
x2 = (-0.5)*a + (height-0.5)*c + tx

連立三元一次方程式2
y0 = (-0.5)*b + (-0.5)*d + ty
y1 = (width-0.5)*b + (-0.5)*d + ty
y2 = (-0.5)*b + (height-0.5)*d + ty

このうち a, b, c, d, tx, ty が求める値。
それ以外は事前にわかっている。
連立方程式は、ガウス=ザイデル法やガウスの消去法等いくつか数値演算で求める方法がある。
効率を考えてガウス=ザイデル法を用いて上式を解くようにしたら、解が収束せずに求められず、ガウスの消去法を用いることにした。
期待通り matrix が求められている。

ただ、オリジナルとは異なり頂点を与える方が方程式を求める分効率が悪い。
直後の描画処理に比べたらそれほどでもないかも知れないが。

投稿者 Takenori : 17:28 | コメント (0) | トラックバック

2012年02月02日

レンダリング済みフォント

レンダリング済みフォントのサポートをどうするか悩んでいたけど対応した。
Java では縦書きフォントが読めない。
Android も調べた範囲では読めない。
後、フォントを共通化する為にもレンダリング済みフォントは有用と言うことで対応した。

FreeType の Java 版があるか探したが見付からず。
自力で FreeType を Java に移植することも考えたが、かなり労力がかかりそう。
FreeType があれば、共通化や縦書きも出来るようになると思うが、対応は大変。
と言うことで、自前でのフォント関係の処理をすることは諦めた。


少メモリ環境下では、ディスク容量やメモリ容量節約のため使用する文字のフォントデータのみ出力したいが、オリジナルのレンダリング済みフォント作成ツールでは指定した文字のフォントデータのみ出力する機能はない。
ツールのソースを読んでもそれっぽい機能はない。
機能追加するのはそれほど難しくなさそう。
作るかどうか悩んでいたら、既にあることを教えてもらった。

吉里吉里レンダリング済みフォント作成ツール改

投稿者 Takenori : 23:35 | コメント (0) | トラックバック

 
Total : Today : Yesterday :