<<前の10件

2010-3-9[火] マイナーなCコンパイラをちょっと触る

フリーでwin環境用exeが作れる、ちょっとマイナーな Cコンパイラをちょろっと触ってみた、というかインストールしてみた時のメモ.

試したのは LLVM-gcc, PCC, LCC, TinyCC, COINS。

(マイナーというと語弊のあるモノもあるけど win環境用のcコンパイラとしては、で)

LLVM-gcc

(wikipedia)

LLVM は、 そのvmな仕組みとか、 オプティマイズ等の実力とか、 使用用途(appleの動向)とか、 GPL(v3)より緩いライセンスがらみとか、 いろいろ話題で今後の発展に期待大なコンパイラ(vm)、 だけど、 win環境で試すには、 mingw-msys 環境に追加する方法になっていて、 ちょっとまだ敷居が高い感じ。

また、残念ながら win 環境向けにはまだ clang バイナリは無いようなので、試せるのは gccフロントエンドのモノのみ。

ただ llvm-gcc側にコンパイル環境として(mingw-gccをベースにした)必要なものは一通り入っているので、mingwコンパイラをインストールしておく必要はない(あってもかまわないけど)。

設定としては、まず msys 環境が必要。 mingw本家TDM等から適当なのをインストール。
うちは 猫研pack のtdm-gcc環境を使ってるのでそこに寄生。
ここでは c:\msys にmsysがあるとする。

LLVMのダウンロード頁から以下の2つを取得

    • LLVM Binaries for Mingw32/x86 (試した時は、v2.6)
    • LLVM-GCC 4.? Front End Binaries for Mingw32/x86 (試した時は gcc-4.2)

ダウンロードしたファイルを適当な場所、たとえば
  x:\llvm-2.6 x:\llvm-gcc-4.2
に解凍したとする。(来月には2.7が出るみたいだけど、今はまだ)

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)

(wikipedia)

かって(ANSI-C以前)の Cの標準だったろう Cコンパイラで、 最近はこちらでC99化していってるらしい。 (こちらもGPLv3の影響で脚光を浴びてるかんじ?)

もともと移植性のいいモノなためか、ちゃんとwin32版も用意されている。
コンパイラ以外はgnuツールなりmingwツール/ライブラリが採用されているようだ。

こちら から、 pcc-20090818-win32.exe をダウンロード。実行してインストール。

とりあえず通常のプログラムフォルダ c:\Program Files (win64なら c:\Program Files (x86)) にインストールしたとする.
※ひょっとすると、win64でも(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用バッチがあったので コンパイルしてみてた。 ので、そのメモも。
ソース修正は1行挿入のみだけど環境設定がちょっと面倒だった。

ソースについては、こちらから 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環境を用いれば済みそう。

と実際にためすと、flex がハング。msys-regex-1.dll が無いと怒られる。mingwのダウンロード頁 から regex の dll 版をダウンロードして解凍(lzmaというあまり普及していない形式なので解凍ソフト(7z)も入れたり). とりだせた msys-regex-1.dll を msys\bin にコピーしたらok。(ああ、うちのmsysはちょっと古くパックの_a004のモノなので最新で直ってる可能性あり)

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) が出来ているハズ。

※bison等のツールの具合によっては、build /cl のみでコンパイルできるかもだが、己の使ったバージョンの場合、bisonに(相対パスの)ファイルが見つからないと怒られた。で、絶対パス指定してみたら回避できた、という状態。

できたexeを試す場合は、配布バイナリをインストールした環境のpcc.exeと置き換えてみれば、で。


また pcc 自身でもコンパイルできる。この場合は、インストールしたpcc環境のbinへパスを通した状態で

build /pcc /pccsrcdir x:\pcc /pcclibssrcdir x:\pcc-libs

を実行.

もし pccのインストールディレクトリを c:\program files\pcc 以外にしていた場合は、build オプションとして /pccdir c:\hoge\pcc のようにインストールしたpccフォルダを追加する必要がある。win64の場合 (x86)付なので指定が必要だろう(/pccdir "%ProgramFiles%" かな)。

LCC

LCC は、こちら が大本。 コンパイラ作成の教材のような感じで、 配布されているものは、ソースのみでバイナリなし。

ただ、これをベースに使える環境にしたパッケージがいくつかあるようで

といったものがある。


こちら にlcc対応のデバッガが公開されてる。

LCC-WIN32

LCC-WIN32 は結構昔からあって、 昔試した時点で、IDE環境完備でデバッガ等の出来もよく、 言語仕様拡張や生成改善等もされていて で独自発展していたのだけれど、 今、サイトをみると頁はあるけれどコンパイラ等へのリンク等がことごとく切れてる。 一応、去年の秋はまだダウンロードできてたのだけど。 開発会社のトップページを みると権利関係が別の会社に移譲されたようなことが書いてあるようで... 現状フリー配布はなくなったってことなのかな(LCC-WIN32系のサイトをあたれば、FTPからのダウンロードがまだできるかもだけど、リンクしないでおく)。


Pelles C

PellesC もLCC-WIN32同様に IDE完備の開発環境になっている。 IDE上でSJIS入力できたり、char文字列中に表やソで\含文字を使っても 実行で文字化けせず表示されてちょっと感動。 デバッガもそこそこいい感じ。ただwatchポイントは貼れず(LCC-WIN32は出来てた)。 プロジェクト設定は DEBUG/RELEASE切り替え等はできないようで、 プロジェクトのオプション設定でコンパイラ頁とリンカ頁のデバッグ設定をそのつど 切り替えないと駄目なよう(たぶん。でも、その程度)。

当然Winアプリ作れる。(ウィザードもあり)
(あとWinCE用の開発環境にもなっている)

コマンドプロンプトで使用するときは、まず、

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 のモノを選ぶのが吉。

※実は tar.gz のを使った。lburg/lburg.c ファイルのみ改行コードが lf(\0a) でなくcr(\0d) だったためコンパイラに怒られた。lfに置換して済ましたけれど、zipのを展開したらcrlfでそもそも出会わないはずの不具合だったorz

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)^\

のように修正.
-Doutp=outP として、マクロ置換しているのは、関数宣言された名前を変数名としてつかったといって怒られたため、その回避。 (outpはx86系のout命令をc用にした名前として昔からある...のでビルトイン関数かなにかの影響だろうか... とりあえずのお試しで回避できたため原因追求せず)
(-MTにしてるから-Ziいらないけど、面倒なので残したまま。逆に-MTdにするでもいいが後述の修正箇所の.lib名もそれにあわせる必要あり)

あと、etc/win32.c の

  • 8行目付近

    #define LCCDIR "\\progra~1\\lcc\\4.1\\bin\\"

    の内容を、実際にlcc.exeをインストールするフォルダに変更.
    とりあえず、お試しなので "x:\lcc42" で作業しているとして、そこを設定。


  • 20行目付近の "libc.lib""libcmt.lib" に修正する。

win32.c を見ての通り、結構、環境をハードコーディングしているので、 本格的に使うならば、いろいろ修正したほうがよさげな雰囲気。
また、この内容から、コンパイラ以外のリンカやライブラリ等は VCに寄生するスタイルのよう。

とりあえず、staticリンクライブラリが使えるエディションならコンパイル可能になったはず。

dllランタイム版でコンパイルする場合は、上記までの修正に対してさらに、

  • makefile.nt の CFLAGS,LDFLAGS の -MT-MD に変更
  • cpp/unix.c の memmove関数の定義を #ifndef _MSC_VER で挟む.
  • etc/lcc.c の extern int getpid(void);宣言を #ifndef _MSC_VER で挟む.
  • etc/win32.c の "libcmt.lib"(元"libc.lib") を "msvcrt.lib" に変更.

を施す。

で、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と。
※ リンカー等はvcのモノを使うので、vcコンソール環境が使える状態で使うこと.

なお、#include をせずに直接 extern でprintfを宣言してるのは、#include <stdio.h> をすると cpp.exe 実行中に帰ってこなくなったため... 追求するのは面倒なので、お試しとしては、これで終わりにしておく。


TinyCC

TinyCC は小さいCコンパイラ。 (コンパイラ本体のc,hソース行数は3万行弱)。

けど、文法は、ほぼフルスペックのC89+α(C99の一部)。 (浮動小数点数も構造体もビットフィールドもあるし long long もある, らしい)

とりあえず、サイトからwin用のバイナリを取得して、適当な フォルダに展開。とりあえず、c:\tcc にいれたとする。

set "PATH=c:\tcc;%path%"

でパスを通して、あとは tcc hello.c のようにしてコンパイルするだけ。 winアプリも出来るよう(examples/hello_win.c )
※ランタイムとしてmsvcrtを使用の様.

すごく、あっさり使えてしまう代物のよう。


※実は試すまで、この作者のotcc の延長のモノかと思ってた。 舐めてました。すみません。 otccのほうはwin用でなくlinux用の 1000行に満たない小さいソースのKR-C系ミニ言語. 実行ファイル生成. (IOCCC 2002優勝らしく. 配布ではちゃんと可読なバージョンもあるのでそれをみるのも吉. それどもトリッキーですが)

TinyCC コンパイラのコンパイル

mingw-gcc を用いる。
mingw-gcc を使える状態にしておいて、 サイトから取ってきたソースのzipを x:\tcc-0.9.?? にでも展開する。

x:\tcc-0.9.??\win32 フォルダに移動して、 build-tcc.bat を実行する。exeができたら吉。


COINS

COINS についてはリンク先の概要をみるなりして。 コンパイラ研究のインフラとしてのCコンパイラが配布されてるよう。 ライセンスはApache Licene Version 2.0

モノは java で作られていて、また win環境では cygwin を使う ので、予め、それらがインストールされている必要がある。

javaに関しては、コンパイラをコンパイルするのでなければ jdk は必要でなく jre がインストールされていればよさそう。何かの折にjre6がインストール済みなのでそれを利用。
c:\Program Files (x86)\java\jre6 にインストールされているとする。

cygwin は gcc等の基本的な開発ツールがインストールされていればよく。 かなり昔にインストール済みなので、新規インストールについては他のサイトを適当にみてもらって。 と自分の環境はしばらく更新してなかったのでsetupで更新... したら、どうも最新のcygwin環境だとCOINSはちょっと不具合がでるようで対処が必要になる.後述)
cygwin は c:\cygwin にインストールされているとする。

COINSコンパイラのインストールや使い方については、こちらこちら やその他諸々を参考に...

ダウンロードはCOINSのダウンロード頁 から、 こちらのサイトの助言にしたがい 国際(en)版をダウンロード。 (jp版は euc が使われているためツールによっちゃwin環境では不味い場合があるらしい... なおjp版だけならSourceForgeの頁 からもダウンロードできる)。
己が入手したのは coins-1.4.4.3-en.jar

ダウンロードしたファイルを(jarだけど)zipファイルとして適当なフォルダに解凍。
とりあえず、 x:\coins\ に解凍したとして、
  x:\coins\coins-1.4.4.3-en
ができているとする。

※この中には classes\ フォルダがあり中身が生成済みだったので、今回はそれをそのまま使うことにした。ソースのみ配布のバージョンなら当然再コンパイルが必要。


最新のcygwin での不具合回避のため、c:\cygwin\bin 中から gcc-3.exe cpp-3.exe
gcc.exe cpp.exe に変名して x:\coins\bin\ フォルダ作ってコピー.

こちらで紹介されてる不具合回避方法だとCOINS以外で不味い場合もあるかも(?)なので、また、今回はhello world のみの簡易なお試しなので暫定的な回避にした。


コンパイルでのコマンドプロンプトでの記述が長くなるので、次の一行をバッチにしておく。

java -class x:/coins/coins-1.4.4.3-en/classes coins.driver.Driver -coins:target=x86-cygwin,assembler=as %1 %2 %3 %4 %5 %6 %7 %8 %9

名前は coins_tc.bat として x:\coins\bin\ にセーブ。


コマンドプロンプト窓(dos窓)をあけ、pathを設定する。

set "PATH=x:\coins\bin;c:\cygwin\bin;%ProgramFiles%\java\jre6\bin;%PATH%"

※不具合回避で置換すべきexeのあるフォルダが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に移動してるので、お試しフォルダへ再度移動。
そこで coins_tc.bat -o hello.exe hello.c を実行すれば、パス名警告は減る(無くなる)かも。


コンパイルして出来上がったexeは、cygwinのgccのライブラリをリンクしているだろうで、当然、その制限をうける。


(※だめもとでmingw環境でコンパイルしたらやっぱり駄目だった。リンク関係でいろいろ無いと怒られる。けど、そのへんの問題だけなのかも?)


その他

  • hello world頁をどうこうしようと思ってやり始めたが面倒になってあきらめ... が、折角試した分くらいは、で書いたのがこの頁。
  • vc,bcc,dmc,watcom はとりあえず、いろんなサイトあるし。
  • Fail-Safe Cというモノもある模様。win用はなさそう?で、cygwinで試しみるも失敗。(どちらかというとocaml関係に振り回される時間のほうが多くorz)
  • COINSしたくてcygwinも使ったが、cygwinだとunix系のモノが試せてしまい、前行のようにダークサイドへ突入...で疲れたので止め。ポート済み以外はみないですまさないと... TenDRAはないのかな。
  • 試して無いけどwinで動くcインタプリタとして Ch とか CINTとか
  • Under C++ とか、 その他諸々以外とある模様。






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();
    略
}

  • unittest.hをincludeして
  • UNITTEST()でテストを書き
  • main()の初っ端で UNITTEST_RUN_ALL() を呼び出す.
  • テスト実行の有無は、コンパイラオプションでのUSE_UNITTESTマクロの定義の有無

といった感じでヘッダファイル一つですむ. そのヘッダの中身は

#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_RUN_ALL()マクロは 空なので main()で何も実行されず、
UNITST_TEST(T) マクロは 関数テンプレートを定義するが 使われず実体化しないので、コードは生成されない。

テストをするときは、
uNITtEST_Mgrクラスが定義されてる。
こいつはstaticメンバーだけで構成していて、 テスト関数へのポインタの配列とその数、 テスト関数をポインタ配列に登録するadd()関数、 登録された関数をすべて実行するrunAll()関数、 がある.

UNITST_TEST()マクロは、ファイルstaticのclass変数を定義にすることで main()実行前にコンストラクタが実行され、 そのコンストラクタ内でテスト関数へのポインタを UnitstMgr::add()で登録している.

グローバル変数のコンストラクタの実行順番は不定なので、Mgrの管理する変数はそれらよりも先に初期化されている必要がありstatic変数にしている(ので登録可能なテスト数/配列サイズも固定)

そして main()関数の開始時点で、uNITtEST_Mgrにテストがすべて 登録済みなので、UNITST_RUN_ALL ( UnitstMgr::runAll ) を実行すれば、テストが全て実行される.

わりと単純にすむ、というか、すませてる.
main()で1文とはいえテスト用に呼出す必要あるし、 テストの最大数を決め打ちする必要があるのは無粋だけど.


まあ、実際に使おうとすると、assertだけでなく、各種チェック用のマクロがそこそこほしくなるし、多少なりとはいえ、経過メッセージをだしたりエラー数カウントしたりしだすと、すぐ1桁以上大きくなってしまったけれど.

[これ]


こんな "オレ仕様" でなく CppUnit なり Google の gtest なり boost のモノなりを 使い慣れたほうが生産的だろうけれど、 慣れる以前に面倒そうで手が出せず、 こんなものを作ってしまった、と (ターゲット環境で使えるものがほしかったてのもあり)

ただ色々付け加えた後に、ひさしぶりに元の(上記の)ソースみると、 蛇足だったかなあ、という気にもなる.

※そういや、グローバルのコンストラクタって処理順保証なしだから マルチスレッドでばらばらに実行される可能性ってあるのかな? (当然そうだと今のままでは破綻)





2010-2-5[金] Win環境でのSTLport 5.2.1のインストールのメモ.

前回の試しで、STLportの結果がよさげだったので VC以外のコンパイラでも試してみたくなり、とりあえず dmc 用を インストールしてみようとしたのだけどハマった。

VC用はググればインストール例あるし configure.bat --help やって、指示通りにすればできるようで一番お手軽だった模様.

まあ付属ドキュメント読まずに適当にやるでは不味かった. 読めば書いてた. でも英語なんで翻訳ソフトでちまちま... 悔しいので適当に気になるところだけ訳ぽくしてみた (わからないところを雰囲気で適当にしてたりするので、ちゃんと元のを参照のこと)
README.windows, README.msvc, README.mingw, README.dmc, build_system.txt,
(最近の修正が反映されてない文章もままあるので注意)

ようは、mingw,dmc,bcc 用を作る場合は、Msys 環境が必要. またmsys版でなくMingw32版のgnu make が必要そう. →dmc,bccが目的でもmingw入れちまうが吉かも.

  • コマンドライン版コンパイラが使えるコンソール窓を用意.
  • msys環境(sh)環境へ遷移(msysフォルダ下のmsys.bat実行)
  • STLport-5.2.1を展開したフォルダ直下の configue 実行
  • build/lib フォルダ下で ming32-make 実行. (できたライブラリを STLport-5.2.1/libへコピー)
  • build/test/unit フォルダ下でming32-make実行.
  • build/bin のテストexeを実行.
  • stlportを使うコンパイラ環境の設定.

て感じか.

以下、作業メモ. ただし、あまりうまくいってません。


※watcom c++ 用の build 環境は用意されていない模様.
(古い記述からするとターゲット名vc6で出来そうに思えたけど結局駄目だった)
※追記: openwatcom の wiki で stlport 5.1.5 のコンパイル済みのモノが実験物として置かれている模様. (ただ dll版のみの様)

http://www.openwatcom.com/index.php/User:Cmeerw

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 ターゲット名

のような感じでメイク.
(bccの場合はmakeのほうがよい? 少なくともcleanは mingw32-make でエラーでて make を使えばokだた)

ターゲット名で install を指定すればallコンパイル&libへのコピーだが、dmcではエラー. また、all は dll(shared)版しかコンパイルしないようなので、結局自分の必要なものを 指定するのが無難そう. (このへんコンパイラごとに事情が違う)

mingw32-make -f ???.mak release-static dbg-static stldbl-static

な感じに、必要なものを列挙してメイク(指定できるものはコンパイラごとに事情が違う).
できた obj/???/*.lib を STLport-5.2.1/lib/ とかにコピー.


あと、テストのコンパイルだけど、日本語環境だとソース中のフランス語文字が壊れたSJIS扱いで怒られるかもで、

ctype_facets_test.cpp 430 付近の'で囲まれた文字を'\xE7'に書き換え

ておく(怒られたのは vc だっけ)

build/test/unit/ に移って

mingw32-make -f ???.mak

で、STLport-5.2.1/bin/にテストプログラム生成、 できた exeを全て実行、出力でエラーがなければok ...なんだが、 エラーでてるかも. ロケール関係ぽいので、とりあえず、あきらめ.


stlportのヘッダや出来たlibを利用するる場合は、 各IDEやコンパイラごとの環境設定するなり、 makefileやプロジェクトファイルごとに設定するなり.


適当に端折ってるので、不味かったらいろいろ試す、で.
ちゃんと使う場合は、configure時のオプションや、 ソースのconfigヘッダ等で、いくつか環境にあわせた設定をしたほうがそさそうです.
(テスト試すところまでで、力尽きた)

dmc

dmcは、ビルドする前に、 dm/bin/ にある link.exe, lib.exe をコピーして
dm_link.exe, dm_lib.exe
を作り、pathの通ったところにおいておく必要がある.

また、makefile の修正が若干必要かもで、
build/makefile/gmake/dmc.mak 111行付近の -p128 を書き換えて

release-static : AR += -p256

のようにする. (256は適当にえらんだ. これでもおこられるならさらに大きくか)

リンク時に文字化けした警告がでてるのが気になるが、libできてるようなんで 気にしないでおく.

ただコンパイル途中で

nbytes=65664, ph_maxsize = 65520
Internal error: ph 1854

なんてエラーがでるかもだが... 出たらあきらめ orz
(-HPでおおきな値設定しても駄目だった)
test/unit でのメイクの hashテストか何かで発生した.
ので、test/unit未確認.

stldbg-shared のコンパイルでも出るかもだが、 dll版は興味ないので放置した (先に static 版のコンパイルをしてあると コンパイル通ったかもだが、それでいいのかで)


コンパイル環境の設定については、dm/bin/sc.ini の INCLUDE,LIBの先頭に

INCLUDE=c:\STLport-5.2.1\stlport;…元の文字列…
LIB=c:\STLport-5.2.1\lib;…元の文字列…

みたいな感じに追記するのも手.
(CD版の dmc の場合、DMが配布している4.5.? の stlport が入っているかもだが、 バージョン違ってファイル名等微妙に違うので、下手に上書き等はしないのが無難)

あるいは、己は、元のと新規とを使いわけたいので 元のsc.iniでは行末に置かれていた %INCLUDE%と%LIB%を先頭にもってきて

INCLUDE=%INCLUDE%;…その他の元の文字列…
LIB=%LIB%;…その他の元の文字列…

のようにして、コンパイラ環境設定のバッチで INCLUDE,LIBの設定を切り替えるようにしてみた.


で、とりあえずいけるはずなんだが... 試しにコンパイルしてみたら .lib が見つからないと怒られた. リンク時の引数で stlport_static.lib とかを指定したらとりあえずリンクできたが、 自動リンクされなかった. ヘッダみるかぎり.libは自動リンクされるはずなんだが. なにか設定抜けてそうだが未調査.

mingw

とりあえず上記でstatic版のみ作れた. dll版は、 リンクエラーがでたので放置. (使ったmingw環境の設定等があわなかったのか?)

test/unitのはコンパイルできて実行できたと思う.

できたlibを使ってのテストは何もしていない状態.
(パスとかオプション設定とか README.mingwみてると面倒そうなんで)

bcc

bccの場合は 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ヘッダフォルダでも
 type_traits

ファイルが、このライセンスになっていた.

これは c++0xから増えるヘッダで、v5.2.1では、これ単体で, 他の部分では Boost 発祥の type_traits がincludeされているため、 このtype_traitsファイルさえ使わなければ、気にしなくていいのだけど...

最近の STLportの git リポジトリからとってきたものをみると、 他のソースもこの type_traits ファイルinclude するようになっていて 結構影響する状態のよう.

実質が今のライセンスと かわるのかどうか...

Wikipediav2.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 等で書いて計測.
表示ルーチンぽくしているが、計測では表示しないので、文字列生成が主な時間.

ソースはこれ, これ, これ

数値→文字列化を持っていない処理では、ltoaを用いて文字列化している.

CharAryStream は、固定サイズのchar配列に結果を書き込むだけにしたカスタムstreambufを使ったもの.
strstreamとほぼ同じだが、その存在を失念していた.で、気づいて計測しなおしたが、vc9付属のはバグがあるようなので、CharAryStreamも残してるしだい(endsの件もあるし).

string, stringstream, CharAryStream では、ローカル変数として関数内に持つ場合と、static変数にして、初期化を一度きりにしたものを試してる.
stringの場合はローカルでの使い方が普通だろうが、streamに関しては static かはともかく、まとまった出力をしている間は1つのインスタンスをずっと使いまわすようなコードになると思うので.

ということで、phenom2 3GHz環境で、vc9 と mingw g++4.4.0 での結果.
100000回の平均. 単位:μ秒.

  vc9[A]vc9[B]mingw[A]mingw[B]
sprintf 0.5401.1710.5071.036
ポインタ操作+memcpy+strlen 0.0580.2130.0740.221
strcpy+strcat 0.0870.3670.0830.298
std::stringで'+'使用 0.7272.0330.9641.270
std::stringで領域予約で'+=' 0.3390.4860.4100.544
std::string↑の変数をstatic化 0.2060.3470.1220.278
stringstreamローカル変数 2.2153.1002.1232.141
stringstream static変数 1.4602.2670.7740.852
strstreamローカル変数 1.8912.3611.3491.400
strstream static変数 0.530---0.3460.408
CharAryStream ローカル変数 1.8622.1990.9841.047
CharAryStream static変数 1.0111.3940.2860.342

[A][B] は、test_sprintfに与える引数の文字列長の違い. 文字列長が違うと、差のありようもかわるので. (一例の結果だけみて、何倍の差があるとか早とちりしないように:)

やっぱり、ポインタ活用や、strcatを用いたものはそこそこ早く. アロケートの(ほぼ?)発生しない stringの '+=' やstream系の<<も まあ早く.
(てか g++ の strstream や CharAryStream の速度ならば、 もう Cできりきりに書かなくていいやん、と)

stringstream も、多少気になるかもだが(毎度ローカル変数生成のような)下手な 書き方さえしなければ、悪い速度じゃなさそう. (VCの stream 実装がいまいちぽいが)

というか、そもそも printf 系の処理はそんな速いものでもなく...


あと、vcでの strstream は [B]の段階で[A]の文字列のままだった. seekp(0,ios::beg)が効いていない模様. 仕様把握してないので言い切れるか微妙かもだが、バグだろう. 速度的には、自前のCharAryStream と大差ないし、 strstreamを追求したいわけじゃないので放置.


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でのデフォルトに合わせるため.
面倒なんで sprintf, CharAryStream, stringstreamだけ、だが、vcのstlの出来がいまいちっぽいのでSTLport(5.2.1)もためしてみた.

ソースはこれ

で結果( 100000回の平均. 単位:μ秒 )

 vc9mingwvc9+STLport
sprintf 2.5832.0622.460
CharAryStream 5.0474.1222.164
stringstream 5.8463.9952.553

vc,mingwとも stringstreamがsprintfの倍くらいにばけてる. test1の結果からすると、必要以上にオーバーヘッドがある気分.
stringstream のかわりに CharAryStream(strstream) を使うのは 若干の固定費削減って感じで劇的は無し、というとこか.

で、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';

真打:-)
printf系で楽に書ける書式が、どうしようもなく面倒になることがstream系を使いたくない 大きな理由だけれど、まあ stream への愚痴は後回しで、

ソースはこれ

で結果(100000回の平均. 単位:μ秒).

  vc9mingwvc9+STLport
sprintf 1.2351.1991.183
CharAryStream 2.1580.5290.647
stringstream 2.9391.4741.105

stream、記述のゴツさに、つい比例してしまいそうな印象をもってしまうが、 書式解析がコンパイル時の型チェックでまかなわれているため、 sprintfより速くなる、可能性がある ... vc標準は無視、したいなぁ:)

stringstreamは STLport が がんばっている、って、ことだろうけど.
(string側の出来もいいってことかな?)


test1改 : アロケート頻度.

test1 において、お手軽な範囲で、無理やり、include関係での malloc や new を乗っ取って、使用数を計測してみた. (ソースこれ追加)

ライブラリ.lib 部分になっているのは手がだせてないので、不正確すぎて、判断するには危険だが、大雑把な目安にはなるかも、で.

といっても、ヘッダのみなんで、vcは値でたけど、mingwはひっかからず.

以下vcでの1回表示のときの、表示部分でのmalloc:freeの呼び出し数.

  [A]malloc:free[B]malloc:free
std::stringで'+'使用 2:211:11
std::stringで領域予約で'+=' 1:11:1
std::string↑の変数をstatic化 0:00:0
stringstreamローカル変数 14:68:8
stringstream static変数 3:25:5
CharAryStream ローカル変数 3:33:3
CharAryStream static変数 0:00:0

vc の string 処理は 16バイトまでならstring内にバッファがあるため、 文字列長でアロケート回数が変わる.

stringstream の[A](初回)の値が多かったりfree数が合わないのは、 ライブラリ側の処理とか、あと、バッファ以外にも、 stream処理のサブクラス等の初回生成が含まれているためだろうか.

処理順の都合、stringstreamでアロケートされているけど、 CharAryStream, stringstreamの順に計測すると、CharAryStream側の 回数増えたりするので、可能なものは共通化されてるよう.

で、stringと CharAryStream のstatic変数版が 0回になっているので アロケートを極力回避しようと思えばできる、って感じ.
(面倒なんで表にしてないけれど、double使った test2 でも 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のもの.

  vc9[A]vc9[B]mingw[A]mingw[B]vc+stlp[A]vc+stlp[B]
std::stringで'+'使用 1.1761.9930.9651.1970.3260.907
std::stringで領域予約で'+=' 0.4940.4400.4120.5480.2810.480
std::string↑の変数をstatic化 0.1710.3310.1260.2900.1270.355
WTL::CStringで+使用 0.8080.946----
WTL::CStringで+=使用 0.3240.463----
WTL::CString Format使用 0.8751.314----

STLport std::string の '+' での結果がこれって、何だ? って感じだ.

STLportはメモリー確保関係工夫してるようなことどっかに書いてあったと思うけど、 いいなあ.


WTL::CString は ある意味順当かな. std::string のシガラミないし設計の方向性が違い (1core環境じゃ)軽めの処理だろうで. (参照カウンタのわりと素直?な実装だし)

static 変数版を試してないのは実装的にメリットないからだけど、 かわりに Format メンバーを使ったものを併記.
Format()の実装はソースみると自前でprintf系実装しているのではなく、 ラッパー. メモリー確保のために、そこそこformat解析してサイズを求めているので、 速度的には確実に sprintf より遅くなるのだけど.(使い勝手のものだから).


結、というか、雑感

たったこれだけのお試しで結論するのも不味いという気もなくはないし、 使い慣れてない iostream系は何かぽかってないか不安もあるけど、 とりあえず.

iostream系は、それだけなら速度パフォーマンスのそう悪い仕組みでない、といったところか.
併用される他の要因で結果的に遅くなりやすいかも、だけど.

でも、実装は工夫が必要なんだろう. vcやg++のものが工夫が足りてない、というより、 STLport ががんばっているんだろうし.

stream よりも string のほうが速度ペナルティが多くなりそうだ. += じゃなくて + で楽したいんだし.

少なくとも string 使ってる奴が速度パフォーマンスを理由に stream系にケチをつけちゃいけないだろう(自戒:)

ただ stringstream は 無様に思えてきた.

strstream は完全に捨て去る必要ないよなあ、で.
strstream は ends の手間が嫌らしいだけで、 str()時、あるいはc_str()なりを新設して、取り出し時に '\0'を付加する モードさえあれば、かなり状況改善されてたんじゃなかろうか. 原理主義的な人たちは滅ぼしたい代物だろうけど、 C文字列を無視できない人間に stream 系売り込むにはベターな存在だったのかも.

vc標準の奴の性能があれなんで躊躇しちゃうけど、 速度を気にするならstrstream(かその代用品)使うのも手かも、と.
※test2,test3はコンストラクタを追い出した状態の結果なので、 コンストラクタの頻度があがるとtest1でのローカル変数版の結果に近づく わけだから気をつける必要はある.


printf系は速くないってのを再認識かな.
書式解析やargvな処理を思うと原理的に... 実装の差もあるだろうけど (大昔のbc遅い実装だった覚えが). 今回は vc, mingw ともprintf系は MS の実装が使われてるはずなので 違うsprintfだとまた違う印象になった可能性もあるだろう.

まあ速くないといっても、この程度の差で困ることは今時まず無いので 気にしないか. (printf系の使用を避けて速度アップを実感できたのは8bit,16bit機時代くらいか).

今回の結果はまさに50歩100歩といったところだし.
ただ、メモリーアロケートの発生をさけた結果なので、 それが頻発する状態はやっぱり気にしたほうがいいとは思う.

デバッグ処理で極力メモリーアロケートなんて起きて欲しくないわけだし.

もちろん、printf系だって全くメモリーアロケートしないかというと 実装しだいだし書式によってはありえるのだけれど. ただ通常極力しないように作られてるし、 デバッグで通常出す程度ならまず起きないし、で.


あと今回は文字列処理をメインにしてたから、ためせてないけれど、 コンソール出力等でiostream系が遅くなる(かもしれない)要因の一つは endl 時にバッファフラッシュの動作が含まれていることだと思う. これも今時のPCでは気にするほどのこともないだろうけど. でも、'\n'と endl を意図的に使い分ける意味もある、ってのは 忘れずに、と.



今回のソース関係の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 ビット版のソースがあったので、 ちょっと速度比較してみた.

ソースは コレコレコレ. (zip)

96ビット版が要修正だったり64ビット版は出力も64ビットだったり だったので 結局 paper みて修正. あと、xorshift.pdfの256ビット版追加. (シード設定可のクラスにしたりでエンバク不安あり. 64,96,160版はA,B,Cの選択がこれでよいのか自信なし).
で paper の160のソース、>>Aになっているけど <<Aの誤記だと思われる. 他と比べてもだし、別のチェックで結果がメタメタになった.

結果は以下. 環境は基本Phenom2 3GHz (玄箱HGは PowerPC 266MHz).
100000000回実行の平均. 単位:ナノ秒.

 vc9
(32bit)
vc9
(64bit)
mingw
g++3.4.5
mingw
g++ 4.4.0
dmc8.51owc 1.8bcc 5.8.2玄箱HG
g++4.1.2
XorShift 32 3.4112.6623.3292.99710.4803.3555.01023.100
XorShift 64 2.0162.1671.8421.9965.1593.0003.73434.700
XorShift 96 2.3354.3451.6672.0415.7733.0233.51238.500
XorShift 128 2.3313.9632.4191.9955.4443.2303.68546.200
XorShift 160 2.3344.2062.3382.0266.2313.0023.18746.200
XorShift 192 2.5843.8742.8762.3368.1153.1573.56646.200
XorShift 256 7.3976.2646.3276.49117.2246.1847.197134.800
線形合同法の乱数 1.5091.3361.6645.7843.5811.3293.15423.200
clib rand 18.55914.17415.08014.7206.5345.2363.231461.900
MT Rand 5.4525.6797.9965.86931.2999.2898.280127.200

下3つは比較用. MT乱数(mt19937ar-cok.c)と 線形合同法を使った乱数とC標準ライブラリのrand()を使ったもの.

コンパイラごとに癖はあるけれど、大筋は似た傾向.

paperの例では1.8GHzで4〜6ナノ秒とあったので、 3GHzの環境(vcやg++)で 2〜4ナノ秒は、そんなものかもしれない.
なお、これら(の速い値)はインライン展開されている(はず). 関数呼び出しになっている場合は、(vcにて) 3〜6ナノ秒台だった (テストルーチンで乱数をファンクタにせずワンクッション関数を噛ましてたとき)

全体に周期32ビット版は他より遅くなっているのは、たぶん(アウトオブオーダーとかのレベルの)命令の並列化ができないためだろうか.

速度的には、 大筋ビット数が増えれば遅くなるけれど、32-192の範囲では 増える周期(質の改善)に比べ時間の増加はさほどでもないので、 どうせなら128でなく160や192あたりを使うのも手かもしれない.

256ビット版はプログラムが少々複雑になっているせいか 時間増加のよう. 128ビット版にくらべ2〜3倍... MTと対して変わらない速度(場合によっては負ける)なので出番はないだろう.

でinline展開で速いのはいいけれど、 ビット数が大きいほど(ささやかとはいえ)コードサイズが膨れやすくなるので、 乱数の質が高くなくていいなら、ビット数少ないのを選ぶのもよいかもしれない.
でも線形合同法でいいなら、(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を大人気なく邪魔したり最後に一言置いていったりと こんなカワイイ人だったんだなあ。 たのしかったけどちょっと短く... おかげでキャンセルしてた 某氏の誕生日オフ会(という名の新年会)に合流できたので 吉としとこう(無理がたたって後ボロボロだったけど)





<<前の10件