|
2010-3-9[火] マイナーなCコンパイラをちょっと触るフリーでwin環境用exeが作れる、ちょっとマイナーな Cコンパイラをちょろっと触ってみた、というかインストールしてみた時のメモ. 試したのは LLVM-gcc, PCC, LCC, TinyCC, COINS。 (マイナーというと語弊のあるモノもあるけど win環境用のcコンパイラとしては、で)
LLVM-gccLLVM は、 そのvmな仕組みとか、 オプティマイズ等の実力とか、 使用用途(appleの動向)とか、 GPL(v3)より緩いライセンスがらみとか、 いろいろ話題で今後の発展に期待大なコンパイラ(vm)、 だけど、 win環境で試すには、 mingw-msys 環境に追加する方法になっていて、 ちょっとまだ敷居が高い感じ。 また、残念ながら win 環境向けにはまだ clang バイナリは無いようなので、試せるのは gccフロントエンドのモノのみ。 ただ llvm-gcc側にコンパイル環境として(mingw-gccをベースにした)必要なものは一通り入っているので、mingwコンパイラをインストールしておく必要はない(あってもかまわないけど)。
設定としては、まず msys 環境が必要。
mingw本家や
TDM等から適当なのをインストール。 LLVMのダウンロード頁から以下の2つを取得
ダウンロードしたファイルを適当な場所、たとえば msys込みで、パスを設定。 set "path=x:\llvm-2.6;x:\llvm-gcc-4.2\bin;c:\msys\bin;%path%;" あとは、llvmc 、または、llvm-gcc, llvm-g++ を用いて、 適当なソースをコンパイル、といった感じだろう。 mingw 環境のコンパイラだけ置き換えた状態のようなんで、 使い方は mingw(gcc)。 win-apiアプリの コンパイルもできるよう。 (簡単な窓を出すだけのhelloプログラムは動いた)。 速度等はろくに調べていないけれど、xorshiftのときのサンプルをコンパイルしてみたところ、inline展開が働いた時のスコアは結構よいほうだった。 ただ、inline 展開をあきらめるのがvcやg++4.4より早いようで、xorshift128あたり から関数呼出になってスコア落ちた。gcc3.4.5同様素直な感じなのかな、と。
PCC(Portable C Compiler)かって(ANSI-C以前)の Cの標準だったろう Cコンパイラで、 最近はこちらでC99化していってるらしい。 (こちらもGPLv3の影響で脚光を浴びてるかんじ?)
もともと移植性のいいモノなためか、ちゃんとwin32版も用意されている。
こちら から、 pcc-20090818-win32.exe をダウンロード。実行してインストール。
とりあえず通常のプログラムフォルダ c:\Program Files
(win64なら c:\Program Files (x86)) にインストールしたとする.
コマンドプロンプト窓でコンパイラを使う場合は、 set "path=%ProgramFiles%\pcc\bin;%path%" を先に実行。この状態で pcc を用いてコンパイル。 pcc --help でヘルプ表示すると ld の分しか表示されず涙目だけど、 元はunix系のccそのものだろうから、そのあたりを見ればいいのか。 (基本部分はgccが同じにしてるはずだろうからgccのを試せばよいのかも) mingwベースなんで、winプログラムも同様。とりあえず窓helloは、 pcc w_p_smp1.c -l gdi32 でいけた。 なお、出来たexeは mingw 同様 msvcrt.dll に依存している。
PCC コンパイラのコンパイル
当初 win32バイナリの配布に気づかず、ソースにwin32用バッチがあったので
コンパイルしてみてた。
ので、そのメモも。
ソースについては、こちらから
pcc-cvs-100306.tgz、こちら から
pcc-libs-cvs-100306.tgz をダウンロード。
展開してできたフォルダを x:\pcc, x:\pcc-libs とする。
ソース修正は pcc/os/win32/config.h の31行目付近の #if !defined(vsnprintf) #include <stdio.h> //この一行を追加. #define vsnprintf _vsnprintf #endif をコメントのように#include <stdio.h> を追加するだけ。 (あとからstdio.hがincludeされるとvsnprintf置換でバグになるため) 面倒なのは bison や flex を使うのでそれらを用意する必要があること。 幸い猫研パックのmsys に入ってるので、msys環境を用いれば済みそう。
vc2008 でコンパイルする場合は call "%VS90COMNTOOLS%vsvars32.bat" をしておき(2005なら%VS80…,2003なら%VS71…だろう) x:/pcc/os/win32 へカレントディレクトリを移動し、 build /cl /pccsrcdir x:\pcc /pcclibssrcdir x:\pcc-libs を実行。うまくいけば pcc.exe (とmkext.exe) が出来ているハズ。
できたexeを試す場合は、配布バイナリをインストールした環境のpcc.exeと置き換えてみれば、で。
build /pcc /pccsrcdir x:\pcc /pcclibssrcdir x:\pcc-libs を実行. もし pccのインストールディレクトリを c:\program files\pcc 以外にしていた場合は、build オプションとして /pccdir c:\hoge\pcc のようにインストールしたpccフォルダを追加する必要がある。win64の場合 (x86)付なので指定が必要だろう(/pccdir "%ProgramFiles%" かな)。 LCCLCC は、こちら が大本。 コンパイラ作成の教材のような感じで、 配布されているものは、ソースのみでバイナリなし。 ただ、これをベースに使える環境にしたパッケージがいくつかあるようで といったものがある。
※ こちら にlcc対応のデバッガが公開されてる。 LCC-WIN32LCC-WIN32 は結構昔からあって、 昔試した時点で、IDE環境完備でデバッガ等の出来もよく、 言語仕様拡張や生成改善等もされていて で独自発展していたのだけれど、 今、サイトをみると頁はあるけれどコンパイラ等へのリンク等がことごとく切れてる。 一応、去年の秋はまだダウンロードできてたのだけど。 開発会社のトップページを みると権利関係が別の会社に移譲されたようなことが書いてあるようで... 現状フリー配布はなくなったってことなのかな(LCC-WIN32系のサイトをあたれば、FTPからのダウンロードがまだできるかもだけど、リンクしないでおく)。
Pelles CPellesC もLCC-WIN32同様に IDE完備の開発環境になっている。 IDE上でSJIS入力できたり、char文字列中に表やソで\含文字を使っても 実行で文字化けせず表示されてちょっと感動。 デバッガもそこそこいい感じ。ただwatchポイントは貼れず(LCC-WIN32は出来てた)。 プロジェクト設定は DEBUG/RELEASE切り替え等はできないようで、 プロジェクトのオプション設定でコンパイラ頁とリンカ頁のデバッグ設定をそのつど 切り替えないと駄目なよう(たぶん。でも、その程度)。
当然Winアプリ作れる。(ウィザードもあり)
コマンドプロンプトで使用するときは、まず、 set "PATH=%ProgramFiles%\PellesC\bin;%PATH%" を実行。cc を用いて cc hello.c といった感じ。win窓helloなら cc /Ze w_p_smp1.c user32.lib gdi32.lib で、とりあえず。
LCC コンパイラのコンパイル大本のLCCはソースしかないけれど、win32様のコンパイルバッチが入ってたので 試してみた... が、少々修正する必要があった。 のでそのメモ。 試したのは v4.2 のもの。tar.gz, tar.Z, zip の3つの圧縮ファイルがあるけれど、 基本同じものだけど、ソースの改行コードが違うかも。 winなら zip のモノを選ぶのが吉。
win向けにコンパイルするのには、makefile.nt を使う。 使用するコンパイラは vc. ただちょっと古いバージョン(vc6ぽい) 向けみたいでvc2008にはすでにないオプションが使われていたり。 makefile.nt の9行目付近からの CFLAGS=-DWIN32 -Zi -MLd -Fd$(BUILDDIR)^\ LD=cl -nologo LDFLAGS=-Zi -MLd -Fd$(BUILDDIR)^\ の部分を BUILDDIR=. CFLAGS=-Doutp=outP -DWIN32 -Zi -MT -Fd$(BUILDDIR)^\ LD=cl -nologo LDFLAGS=-Zi -MT -Fd$(BUILDDIR)^\
のように修正. あと、etc/win32.c の
win32.c を見ての通り、結構、環境をハードコーディングしているので、
本格的に使うならば、いろいろ修正したほうがよさげな雰囲気。
とりあえず、staticリンクライブラリが使えるエディションならコンパイル可能になったはず。 dllランタイム版でコンパイルする場合は、上記までの修正に対してさらに、
を施す。 で、vc2008を使ってコマンドプロンプトでコンパイル
call "%VS90COMNTOOLS%vsvars32.bat" を実行(2005なら%VS80…,2010なら%VS100…って感じか) 作業場所が x:\lcc42 フォルダだとして、そこに移動して
nmake -f makefile.nt clean nmake -f makefile.nt all を実行. 上手くいけばlcc.exe,cpp.exe等が作られてる(ハズ)。 お試しは
extern int printf(const char* fmt, ...);
int main(int argc, char* argv[]) {
printf("hello world\n");
return 0;
}
を hello.c としてセーブ. lcc hello.c
でコンパイル。
うまくいけば a.exe ができるので、a.exeを実行してhello worldが表示できたらokと。
なお、#include をせずに直接 extern でprintfを宣言してるのは、#include <stdio.h> をすると cpp.exe 実行中に帰ってこなくなったため... 追求するのは面倒なので、お試しとしては、これで終わりにしておく。
TinyCCTinyCC は小さいCコンパイラ。 (コンパイラ本体のc,hソース行数は3万行弱)。 けど、文法は、ほぼフルスペックのC89+α(C99の一部)。 (浮動小数点数も構造体もビットフィールドもあるし long long もある, らしい) とりあえず、サイトからwin用のバイナリを取得して、適当な フォルダに展開。とりあえず、c:\tcc にいれたとする。
set "PATH=c:\tcc;%path%"
でパスを通して、あとは tcc hello.c のようにしてコンパイルするだけ。
winアプリも出来るよう(examples/hello_win.c )
すごく、あっさり使えてしまう代物のよう。
TinyCC コンパイラのコンパイル
mingw-gcc を用いる。 x:\tcc-0.9.??\win32 フォルダに移動して、 build-tcc.bat を実行する。exeができたら吉。
COINSCOINS についてはリンク先の概要をみるなりして。 コンパイラ研究のインフラとしてのCコンパイラが配布されてるよう。 ライセンスはApache Licene Version 2.0。 モノは java で作られていて、また win環境では cygwin を使う ので、予め、それらがインストールされている必要がある。
javaに関しては、コンパイラをコンパイルするのでなければ jdk は必要でなく jre がインストールされていればよさそう。何かの折にjre6がインストール済みなのでそれを利用。
cygwin は gcc等の基本的な開発ツールがインストールされていればよく。
かなり昔にインストール済みなので、新規インストールについては他のサイトを適当にみてもらって。
と自分の環境はしばらく更新してなかったのでsetupで更新...
したら、どうも最新のcygwin環境だとCOINSはちょっと不具合がでるようで対処が必要になる.後述)
COINSコンパイラのインストールや使い方については、こちら やこちら やその他諸々を参考に...
ダウンロードはCOINSのダウンロード頁 から、
こちらのサイトの助言にしたがい 国際(en)版をダウンロード。
(jp版は euc が使われているためツールによっちゃwin環境では不味い場合があるらしい... なおjp版だけならSourceForgeの頁 からもダウンロードできる)。
ダウンロードしたファイルを(jarだけど)zipファイルとして適当なフォルダに解凍。
名前は coins_tc.bat として x:\coins\bin\ にセーブ。
set "PATH=x:\coins\bin;c:\cygwin\bin;%ProgramFiles%\java\jre6\bin;%PATH%"
hello.cとして以下を適当なフォルダに作成しておく.
#include <stdio.h>
int main() {
printf("Hello World!\n");
return 0;
}
先のバッチを使い coins_tc.bat -o hello.exe hello.c のようにコンパイル。出来た hello.exe を実行して、hello world が出力されたらok. cygwin ツールを cygwin 環境以外で使うため、パス関係の警告がいろいろ出るけど無視。 もし cygwin 環境で実行したいならば、この(上記path設定の)状態で bash --login -i
を実行。カレントがhomeに移動してるので、お試しフォルダへ再度移動。
その他
[コメント]
2010-3-1[月]ありすとBOBO(川崎康宏)読了川崎康宏『ありすとBOBO』いまごろ読了(書店で見かけて知ったのが昨日)。 「ラ、ラブコメよ、ラブコメ。でも、ちょっとだけラブコメじゃないの」 と言ってみたくなる (某PG格言を久しぶりにみて。その元ネタは実はやってないけれどヤっぱ何にでも使えるテンプレートだなぁ... ああ発売日って2週間ほど前だったのか、時期はずした模様)。 いや、言ってみただけです、相変わらずの作風で(ちょっとじゃなくて十分)、クマが主人公。 ええ、Aliceの続編の様。 内容的にはAlice未読でも前作が存在するなんておもわないだろー程度に 独立してるので(もっと匂わしてくれてもいいのに) リメイクの可能性も浮かぶけれど、 作者BBS で1週間くらいしかたってないと書かれてる:) なにげに重要情報の出るBBSです。 続刊もすぐらしい...念願の3巻突入できるのでせうか(変則換算だけど)。 ぜひつづいてほしいなあ。 Aliceのほうは中表紙すばらしいのに表紙が大人しくて勿体なかったけど、 今回は絵もオビもよかったし。 なんというか騙されて(語弊大)買う人もいてくれそうな、で。 鮎川はぎの『横柄巫女と宰相陛下1-3』言葉足らずで失言通りこして横柄に見られてしまう巫女見習いが王位継承問題に巻き込まれる話(1巻)。わりと王道の少女小説か。 すぐさま続刊に手を出す程度には気に入ってしまった模様。 己にしては珍しく特定の声で読めてしまってるし(ナガトでなく千秋). 那須雪絵『魔法使いの娘(8)』最終巻。 こういうラストをかける人なんだよなあ。 エビアンワンダー(おがきちか)と近いものがある、ってか、 視点的に似てるのか。細かくみると結構違うんだけれど... というか素直な少年(スカとかハリとか内沢とか)は描く気ないのかなあ。 ああ悪い方向にそのまんま年食ったのが無山なんだろーけど、 挫折してヒネてしまった野郎ばかりのような。
(新刊嬉しくてついひさしぶりに書いてみたけど、やっぱりシンドイ。 前のは選択なんて考えず無差別だからかけてたんだなあ)
2010-2-25[木] も一度「消失」いーちゃんもみーくんも大っ嫌いだ、 と『クビシメロマンチスト』『嘘つきみーくんと壊れたまーちゃん3』を連続読了してしまった己の選択眼のなさを棚に上げて言ってみる。 (現実逃避的に積本くづしてた罰とは思わない) で、口直しに(というと嘘だけど)『涼宮ハルヒの消失』をもう一度みてきた。 前回みたあと、 ネットで感想みたり原作を一通り読み返した勢いとか、 前回寝不足で見たのを若干後悔だったってのもあり。 最前列のど真ん中の席という 己にとっては最上のポジションで見たかったというのもあり。 一点を集中してみるとかなりの部分を 認識していない状態になるけど、 視覚いっぱいに画面がくる感じが好きなので。
作品自体の感想は前回と変わらず、
やはり表情を堪能。
(多少オーバー気味に感じることもあるけれど.
)。
無意識にみてると、ついつい話者のほうをみてしまっている
ものなので、今回は意識的に しかし、原作よりいい、というか、 原作ver1.3位のヴァージョンアップ版な感じだ。 (これは消失にかぎらず憂鬱もだけど)。 原作どおりの(と感じられる)出来の作品は、 原作ファン的には至極しあわせだけれど、 映画としては陰謀へのもちこし分が 不完全燃焼感を誘発してる感じもあり (へたに顔ボカさずセリフ時(後)にはっきりその場で表情をみせてくれてたら あるいは病室で絵で回想してくれたら印象ちがったのだろうけど)
2010-2-18[木] 「消失」みてきた
「涼宮ハルヒの消失」をみてきた。 劇場アニメでした。 原作で知っていても、目の前で表情が見れるというのは幸せです。 長門がよいのはある意味期待通りの規定事項だけど、 あっちのハルヒの変化も気持ちよく、 うまいタイミングになってます。 映画としては尺長いのだろうけど、 お話としては過不足なく大満足。 小説より満足度あるかもね。 正直、映画見るまでは無理せずTV2期で「消失」やってくれてたら もっと盛り上がってただろうしDVD揃えたのに、 くらいにしか思ってなかったけど、 劇場版クオリティのものを劇場でみれる幸せを堪能、でした。 作品単体の出来の絶対値的には、すごく幸運のよう. (90点が99点になる感覚かな。 見る側のコストパフォーマンスとか、 売り手の皮算用はしらね、で。 160分って6,7話分か. TV2期は「消失」をするかしないかが問題だったんだなあ).
2010-2-13[土] unittestの作成メモD言語の unittest に触発されて、 C++用に真似たものを作ってみたことがある. 使い方はこんな感じ
// テスト例
#include "unittest.h"
class Foo { 略 };
UNITTEST(Foo_Test) {
Foo foo;
assert(foo.hoge() == 1);
略
}
// 起動ルーチン
#include "unittest.h"
int main() {
UNITTEST_RUN_ALL();
略
}
といった感じでヘッダファイル一つですむ. そのヘッダの中身は
#ifndef UNITTEST_H
#define UNITTEST_H
#include <cassert>
#ifndef UNITTEST_MAX
#define UNITTEST_MAX 256 ///< 登録できるテストの数
#endif
#ifndef USE_UNITTEST // テストしないとき.
#define UNITTEST_RUN_ALL()
#define UNITTEST(T) template<typename D> void uniTTesT_dmy##T()
#else // テストするとき.
#define UNITTEST_RUN_ALL() uNITtEST_Mgr<>::runAll()
#define UNITTEST(T) \
class T { \
public: T() {uNITtEST_Mgr<>::add(&test);} \
private: static void test(); \
}; \
static T uNITtEST_vAr_##T; \
void T::test()
template<int N=UNITTEST_MAX>
class uNITtEST_Mgr {
static unsigned tblNum_;
static void* tbl_[N]; //関数ポインタの配列だとvcが落ちたorz.
public:
static void add(void (*fnc)()) {
if (tblNum_ < N)
tbl_[tblNum_++] = (void*)fnc;
else
assert(0 && "too many UNITTEST.");
}
static void runAll() {
for (unsigned i = 0; i < tblNum_; i++)
((void (*)())tbl_[i])();
}
};
template<int N> unsigned uNITtEST_Mgr<N>::tblNum_=0;
template<int N> void* uNITtEST_Mgr<N>::tbl_[N];
#endif // USE_UNITTEST
#endif // UNITTEST_H
テストしないときは、
テストをするときは、 UNITST_TEST()マクロは、ファイルstaticのclass変数を定義にすることで main()実行前にコンストラクタが実行され、 そのコンストラクタ内でテスト関数へのポインタを UnitstMgr::add()で登録している. グローバル変数のコンストラクタの実行順番は不定なので、Mgrの管理する変数はそれらよりも先に初期化されている必要がありstatic変数にしている(ので登録可能なテスト数/配列サイズも固定) そして main()関数の開始時点で、uNITtEST_Mgrにテストがすべて 登録済みなので、UNITST_RUN_ALL ( UnitstMgr::runAll ) を実行すれば、テストが全て実行される.
わりと単純にすむ、というか、すませてる.
まあ、実際に使おうとすると、assertだけでなく、各種チェック用のマクロがそこそこほしくなるし、多少なりとはいえ、経過メッセージをだしたりエラー数カウントしたりしだすと、すぐ1桁以上大きくなってしまったけれど. [これ]
ただ色々付け加えた後に、ひさしぶりに元の(上記の)ソースみると、 蛇足だったかなあ、という気にもなる. ※そういや、グローバルのコンストラクタって処理順保証なしだから マルチスレッドでばらばらに実行される可能性ってあるのかな? (当然そうだと今のままでは破綻)
2010-2-5[金] Win環境でのSTLport 5.2.1のインストールのメモ.
前回の試しで、STLportの結果がよさげだったので VC以外のコンパイラでも試してみたくなり、とりあえず dmc 用を インストールしてみようとしたのだけどハマった。 VC用はググればインストール例あるし configure.bat --help やって、指示通りにすればできるようで一番お手軽だった模様.
まあ付属ドキュメント読まずに適当にやるでは不味かった.
読めば書いてた. でも英語なんで翻訳ソフトでちまちま...
悔しいので適当に気になるところだけ訳ぽくしてみた
(わからないところを雰囲気で適当にしてたりするので、ちゃんと元のを参照のこと)
ようは、mingw,dmc,bcc 用を作る場合は、Msys 環境が必要. またmsys版でなくMingw32版のgnu make が必要そう. →dmc,bccが目的でもmingw入れちまうが吉かも.
て感じか. 以下、作業メモ. ただし、あまりうまくいってません。
mingw,dmc,bcc共通といいつつ基本 dmc しか作業できてないので... configue実行だけど、MSys環境で configure --use-compiler-family=??? な感じで ??? に gcc, dmc, bcc を設定.('='も忘れずに、と). 必要に応じてその他オプションも併記して. configureやったあとに build/lib/ で mingw32-make -f ???.mak clean mingw32-make -f ???.mak depend mingw32-make -f ???.mak ターゲット名
のような感じでメイク. ターゲット名で install を指定すればallコンパイル&libへのコピーだが、dmcではエラー. また、all は dll(shared)版しかコンパイルしないようなので、結局自分の必要なものを 指定するのが無難そう. (このへんコンパイラごとに事情が違う)
mingw32-make -f ???.mak release-static dbg-static stldbl-static
な感じに、必要なものを列挙してメイク(指定できるものはコンパイラごとに事情が違う).
ctype_facets_test.cpp 430 付近の'で囲まれた文字を'\xE7'に書き換え ておく(怒られたのは vc だっけ) build/test/unit/ に移って mingw32-make -f ???.mak で、STLport-5.2.1/bin/にテストプログラム生成、 できた exeを全て実行、出力でエラーがなければok ...なんだが、 エラーでてるかも. ロケール関係ぽいので、とりあえず、あきらめ.
dmc
dmcは、ビルドする前に、
dm/bin/ にある link.exe, lib.exe をコピーして
また、makefile の修正が若干必要かもで、
release-static : AR += -p256 のようにする. (256は適当にえらんだ. これでもおこられるならさらに大きくか) リンク時に文字化けした警告がでてるのが気になるが、libできてるようなんで 気にしないでおく. ただコンパイル途中で nbytes=65664, ph_maxsize = 65520 Internal error: ph 1854
なんてエラーがでるかもだが... 出たらあきらめ orz stldbg-shared のコンパイルでも出るかもだが、 dll版は興味ないので放置した (先に static 版のコンパイルをしてあると コンパイル通ったかもだが、それでいいのかで)
INCLUDE=c:\STLport-5.2.1\stlport;…元の文字列… LIB=c:\STLport-5.2.1\lib;…元の文字列…
みたいな感じに追記するのも手. あるいは、己は、元のと新規とを使いわけたいので 元のsc.iniでは行末に置かれていた %INCLUDE%と%LIB%を先頭にもってきて INCLUDE=%INCLUDE%;…その他の元の文字列… LIB=%LIB%;…その他の元の文字列… のようにして、コンパイラ環境設定のバッチで INCLUDE,LIBの設定を切り替えるようにしてみた.
mingwとりあえず上記でstatic版のみ作れた. dll版は、 リンクエラーがでたので放置. (使ったmingw環境の設定等があわなかったのか?) test/unitのはコンパイルできて実行できたと思う.
できたlibを使ってのテストは何もしていない状態.
bccbccの場合は bcc32.cfg と ilink32.cfg の -L に psdk のpath(???/lib/psdk とか)が 抜けていたらそれを追加. とりあえずdll版は作成できるけど、tlibで怒られてしまいstatic版は作れてない(未調査) でもって bcc v5.5.1 のせいかテストでコンパイルできず.. 面倒になったので挫折. (readme.bccもちゃんとみてないので)
その他のテキストインストールに関係ないけど、気になった文章の適当訳も. README.utf8, stlport_namespaces.txt,
Academic Free License ("AFL") v. 3.0 なファイル
makefile 弄ってたらふと、ファイル先頭に、ライセンスとして
Academic Free License 3.0 となっていて、検索すると、stlportヘッダフォルダでも
これは c++0xから増えるヘッダで、v5.2.1では、これ単体で, 他の部分では Boost 発祥の type_traits がincludeされているため、 このtype_traitsファイルさえ使わなければ、気にしなくていいのだけど... 最近の STLportの git リポジトリからとってきたものをみると、 他のソースもこの type_traits ファイルinclude するようになっていて 結構影響する状態のよう. 実質が今のライセンスと かわるのかどうか... Wikipedia や v2.0の和訳をみてると、 微妙に面倒くさそうに思える...(読解力無くて読み違えてるかも...)
2010-2-4[木] sprintf,string,stream のメモ.去年だか一昨年ぐらいから、もうそこそこパワーあるのだから、と、 ターゲット環境でc++で string 系を使ってたのだけど、 そのとき不精して、デバッグログでも使い始めたら やたら処理落ちする箇所の原因になってしまったことがある. ログといっても シリアル とか USB とか LAN とか経由しない、 数十KBのメモリに溜め込むだけの処理だったんで、 開発中は release コンパイルでも有効にしていて、 多少重くなるといってもI/Oからまないから大事ないと思って 見過ごしてしまっていたのだった. たしかに想定してた使用量を超えたルートができていたけれど、 それ以上に酷い重さに化けていた. もちろんログをとってることが問題ではなくて、 string系の文字列をつかって記述で楽して
dbgmsg = strFmt("%s (%d) :",fname,line) + msg + '(' + a + ")\n";
のような感じに + 等使って、テンポラリ変数発生、メモリーアロケート発生、 が山ほど起きていたのが敗因. (↑は今適当に書いたので実際のじゃない) 文字列専用にアロケータ用意してフラグメンテーション対策はほどこして いたけど、当然速度的にはよろしいもんじゃなく. わかってみればあたり前だけど、思い込んだら、でなかなか気づけなかった. そのときは、デバッグログからstring系一層してsprintf系で やりすごしたのだけど、 たとえば + でなく += を使えばテンポラリ無くなり アロケート頻度は落ちるし、 キャパシティをある程度コントロールしてアロケート発生させなければ、 書式なしの文字列連結ならsprintfより速くなる 可能性だってあるはず. 文字列連結を一個一個指定することになって記述が面倒だけど ... iostream系の << なんかはやってること的には += と似たようなものの はずなんだよね. などと思うと、stream関係って、実は速度的に 悪いもんじゃないかも、って気がしてきたのだった. て、ことで、例のごとく、ちょっと計測してみた.
test1:
sprintf(buf, "%s(%d) : test> %s", fname, line, msg);
sprintf だとこんな感じになる処理を、ポインタ操作+mem系, str系, std::string, stringstream, strstream, カスタムstream 等で書いて計測.
ということで、phenom2 3GHz環境で、vc9 と mingw g++4.4.0 での結果.
[A][B] は、test_sprintfに与える引数の文字列長の違い. 文字列長が違うと、差のありようもかわるので. (一例の結果だけみて、何倍の差があるとか早とちりしないように:)
やっぱり、ポインタ活用や、strcatを用いたものはそこそこ早く.
アロケートの(ほぼ?)発生しない stringの '+=' やstream系の<<も
まあ早く.
stringstream も、多少気になるかもだが(毎度ローカル変数生成のような)下手な 書き方さえしなければ、悪い速度じゃなさそう. (VCの stream 実装がいまいちぽいが) というか、そもそも printf 系の処理はそんな速いものでもなく...
test2 : 浮動小数点数を使ってみる
sprintf(buf, "%s(%d) : test (%7.5f,%7.5f)%c %s"
, fname, line, d1, d1, ch1, msg);
s_stringstream << fname << '(' << line << ") : test ("
<< d1 << ',' << d1 << ')' << ch1 << ' ' << msg;
double を使ってみた例.
↑で%7.5fにしてるのはstreamでのデフォルトに合わせるため. ソースはこれ で結果( 100000回の平均. 単位:μ秒 )
vc,mingwとも stringstreamがsprintfの倍くらいにばけてる.
test1の結果からすると、必要以上にオーバーヘッドがある気分.
で、STLport版、sprintfと大差なく... なんか速い. 細かいことはあとにして、とりあえず、次.
test3 : 幅指定等
sprintf(buf, "%-24s(%10d) : test %#016llx\n", fname, line, val);
s_stringstream << setw(24) << left << fname << right
<< '(' << setw(10) << line
<< ") : test "
<< setfill('0') << setw(16) << hex << showbase << val
<< '\n';
真打:-)
ソースはこれ で結果(100000回の平均. 単位:μ秒).
stream、記述のゴツさに、つい比例してしまいそうな印象をもってしまうが、 書式解析がコンパイル時の型チェックでまかなわれているため、 sprintfより速くなる、可能性がある ... vc標準は無視、したいなぁ:)
stringstreamは STLport が がんばっている、って、ことだろうけど.
test1改 : アロケート頻度.test1 において、お手軽な範囲で、無理やり、include関係での malloc や new を乗っ取って、使用数を計測してみた. (ソースこれ追加) ライブラリ.lib 部分になっているのは手がだせてないので、不正確すぎて、判断するには危険だが、大雑把な目安にはなるかも、で. といっても、ヘッダのみなんで、vcは値でたけど、mingwはひっかからず. 以下vcでの1回表示のときの、表示部分でのmalloc:freeの呼び出し数.
vc の string 処理は 16バイトまでならstring内にバッファがあるため、 文字列長でアロケート回数が変わる. stringstream の[A](初回)の値が多かったりfree数が合わないのは、 ライブラリ側の処理とか、あと、バッファ以外にも、 stream処理のサブクラス等の初回生成が含まれているためだろうか. 処理順の都合、stringstreamでアロケートされているけど、 CharAryStream, stringstreamの順に計測すると、CharAryStream側の 回数増えたりするので、可能なものは共通化されてるよう.
で、stringと CharAryStream のstatic変数版が 0回になっているので
アロケートを極力回避しようと思えばできる、って感じ.
stringstream は、static版でも アロケートが発生しているので、 vc版の測定結果が遅い原因だろう. 逆にいえば、g++や STLportのものは、メモリーアロケート発生させてない、 ということでよいか.
test1 追加 : WTL::CString, STLportのstd::string, の追試やっぱり STLportのstd::stringが気になったので、test1の追試. ついでに WTL::CString の結果も. (test1書き直し面倒なんで... vc9,mingwの測定値が若干違うのも面倒で) STLportは v5.2.1 (staticリンク), WTLは8.1のもの.
STLport std::string の '+' での結果がこれって、何だ? って感じだ. STLportはメモリー確保関係工夫してるようなことどっかに書いてあったと思うけど、 いいなあ.
static 変数版を試してないのは実装的にメリットないからだけど、
かわりに Format メンバーを使ったものを併記.
結、というか、雑感たったこれだけのお試しで結論するのも不味いという気もなくはないし、 使い慣れてない iostream系は何かぽかってないか不安もあるけど、 とりあえず.
iostream系は、それだけなら速度パフォーマンスのそう悪い仕組みでない、といったところか.
でも、実装は工夫が必要なんだろう. vcやg++のものが工夫が足りてない、というより、 STLport ががんばっているんだろうし. stream よりも string のほうが速度ペナルティが多くなりそうだ. += じゃなくて + で楽したいんだし. 少なくとも string 使ってる奴が速度パフォーマンスを理由に stream系にケチをつけちゃいけないだろう(自戒:) ただ stringstream は 無様に思えてきた.
strstream は完全に捨て去る必要ないよなあ、で.
vc標準の奴の性能があれなんで躊躇しちゃうけど、
速度を気にするならstrstream(かその代用品)使うのも手かも、と.
まあ速くないといっても、この程度の差で困ることは今時まず無いので 気にしないか. (printf系の使用を避けて速度アップを実感できたのは8bit,16bit機時代くらいか).
今回の結果はまさに50歩100歩といったところだし.
デバッグ処理で極力メモリーアロケートなんて起きて欲しくないわけだし. もちろん、printf系だって全くメモリーアロケートしないかというと 実装しだいだし書式によってはありえるのだけれど. ただ通常極力しないように作られてるし、 デバッグで通常出す程度ならまず起きないし、で.
今回のソース関係のzip : [download]
2010-2-2[火]箕面(imax)でアバター見てきた。 お話はともかく、映像は気持ちよかったです。 とくに1/1スケールくらいで表示される時は心地よく。 スケールの違うバストアップに切り替わってああ映画だったなあ、と。 (手に届きそうなウォーカーマシンがうれしく)
2010-1-26[火] xorshift乱数のメモXorShift乱数は、こちらとかのように短い関数で紹介されるけれど、たいていシード固定で書かれていて、シードを外部から与えたい場合はどうしたら、と悩んでしまう. もちろん、ちょっとぐぐったらシード設定できるソース載せてるサイトあるし、 おおもとのpaper に all 0でなければよいようなことかいてあるようだけど、 どの程度適当でよいのか不安にもなる. で、今さらながら2chの擬似乱数2 をみたら76に解説&seed設定付ソースがあった. シードは極端な設定をすると不自然な部分列が出力されるからMTのを参考にしたとある. ありがたく、これ流用させていただくことに... と、よくよくみれば、ここの初期化とほぼ同じ(改良版)のよう. その他、検索したときのサイトメモ. mt-liteの実行速度に xorshiftを含む各種乱数の速度比較あり. 良い乱数・悪い乱数に XorShiftと他の乱数との速度比較とかSSE2使ったバージョンとかがある. 同サイトの こちらにもいろいろなコンパイラでの測定比較あり. xorshift.pdf 上記サイトで、"問題があると書かれている" とあったのでみた(翻訳ソフトでわからないなりに). 元のpaperより突っ込んだ性能テストをして、通過しないテストがいくつか(も)あるよう. (XorShiftは3つのシフト数の有効な値の組み合わせがいくつか(も)あるけれど、 組み合わせによって性能にばらつきがあるよう) あと改良版として256ビット版が載ってる. (周期のビット数だけでなく、計算でのシフト数も増やして精度をあげている)
XorShift乱数の速度擬似乱数2の 42 に 周期 32,64,96,128,160,192 ビット版のソースがあったので、 ちょっと速度比較してみた.
結果は以下. 環境は基本Phenom2 3GHz (玄箱HGは PowerPC 266MHz).
下3つは比較用. MT乱数(mt19937ar-cok.c)と 線形合同法を使った乱数とC標準ライブラリのrand()を使ったもの. コンパイラごとに癖はあるけれど、大筋は似た傾向.
paperの例では1.8GHzで4〜6ナノ秒とあったので、
3GHzの環境(vcやg++)で 2〜4ナノ秒は、そんなものかもしれない.
全体に周期32ビット版は他より遅くなっているのは、たぶん(アウトオブオーダーとかのレベルの)命令の並列化ができないためだろうか. 速度的には、 大筋ビット数が増えれば遅くなるけれど、32-192の範囲では 増える周期(質の改善)に比べ時間の増加はさほどでもないので、 どうせなら128でなく160や192あたりを使うのも手かもしれない. 256ビット版はプログラムが少々複雑になっているせいか 時間増加のよう. 128ビット版にくらべ2〜3倍... MTと対して変わらない速度(場合によっては負ける)なので出番はないだろう.
でinline展開で速いのはいいけれど、
ビット数が大きいほど(ささやかとはいえ)コードサイズが膨れやすくなるので、
乱数の質が高くなくていいなら、ビット数少ないのを選ぶのもよいかもしれない. サイズきになるならinlineしない形に書いて160,192あたりを... もっとも、このソースの64,96,160(,192)は使ったパラメータがほんとにこれがいいのかわかっていないので 結局 128 を選んでおくのがベターな気はする.
あと、XorShiftの件とはずれるが、vcやg++でのcライブラリの rand()の速度が遅いのは、 マルチスレッド対応のオーバーヘッドのためだろうか. インライン展開された線形合同法の乱数の10倍前後なのは ペナルティが大きいとも、10数ナノ程度ならば大抵たいしたことないとも、どちらとも. 結局使用量/使用箇所しだい、だけれど、どっちにしろ精度のことを思えば、 とっとと XorShiftなりMTなり用意して用途別にインスタンスを管理したほうが、 楽になれると思う. (参考サイトにある SIMD版のSFMTとかが使える環境なら, そっちのほうがいいだろうな)
追記乱数の精度の評価についてはさっぱりなんですが (自分が使った処理において出目に不都合があるかどうかくらいなら、ともかく)、 同根のtest2をしたときの結果はあまりよいという感じではなかった.(この場合のテストとして適切かどうかもわかってないけど) まあ、たいていの場合の乱数の利用頻度や精度を思うと 普通は MT を選んどくのが無難だと思う.
この程度の速度差(時間差)やプログラムサイズ差が
ネックになるような利用頻度(仕様)やターゲット環境なんて
ゲームでもまずないだろうし.
2010-1-23[土]
今日は睡蓮を堪能。 ぎりぎりについたらぎっしり。 思いのほか男性客がいた、って女性ボーカルなのだから当然か. (それでも女性のほうがおおそうだけれど) 高い人の後ろになったため正面のボーカルと左側はあまり見えず. けど右側はみえた(満足)。 MCを大人気なく邪魔したり最後に一言置いていったりと こんなカワイイ人だったんだなあ。 たのしかったけどちょっと短く... おかげでキャンセルしてた 某氏の誕生日オフ会(という名の新年会)に合流できたので 吉としとこう(無理がたたって後ボロボロだったけど)
|