2014-6-20[金] 古PC に 軽いlinux を入れる
P3-800 世代に kona linux
あいかわらずタマに軽いOS(ディストリ)をインストールしたりして遊んでいる。
最近のお気に入りは konalinux (v2.3 2014-06-03|12付近)。
軽さというならば TinyCore Linux(TinyCorePlus) のほうが頭一つ抜けてる感じだけど、 live-cd ベース オンメモリ運用前提で用意された範囲外のことをしようとすると手間が大きく(てか やれてない)、 その点 konalinux はHDDインスールのdebian環境で普通に apt-get 等できるので普段の延長で弄れて遊び甲斐あり。(いや、すぐ放置になるのだけど)
選ぶ kona のエディションは、メモリが384MB以上あれば full(-i386)版、256MB以上あれば light(lite)版、それ以下(でも192MBほしい) なら black版、
といったところだろうか。もう数世代後のスペックのマシンなら リッチなWMのエディション選ぶのも。
firefox を常用してるので、最終的には firefox (や Libre Office) が最初から入ってる full版を選択した。 ※手持ちの NetVista は CD-drive かつ USB ブートのできない機種だったので、full版を入れるため一時的に 3口のIDEケーブルさしてDVD接続でしのいだが(電源はIDE->USB変換機器のものを流用)、CD|FDD で boot してから USB 起動するツールもあるようなので、なんとでもなりそう。 ※ konalinux の配布物はバージョン名が同じ(2.3)まま ちょくちょく差し替わっているので、ダウンロード時期によって微妙に状況変わってることあり。
P2-400 世代
で、最近、親戚のとこの古いデスクトップPCが2台ほど巡り巡ってウチにやってきた。 デスクトップでも静音電源が当たり前になって久しかったので... 昔は こんなうるさかったんだよなあ。 P3-800のNetVistaも音してるけど、一応静音タイプの代物で、十数年前のPCの常識で思えば静かな部類だったのだと再確認。
とりあえず HDD 換装して kona linux いれてみることにするが、80GB とか 120GB のHDDが認識されない...
どうやら 32GB が最大の頃の代物らしい。10GBや30GBのHDDを繋ぐと認識(80,120GBでもジャンパで32GBに設定すれば使えたはず)。
さて、肝心の kona linux だけど... もっさり。
と、 TinyCore Linux を動かすと win98やwin2k に匹敵する体感。さっすが。
この世代については、下手なことせず win98/win2k 動かしてるほうが幸せ、でも GUI有 linux 動かしたいなら TinyCore Linux (TinyCorePlus) あたり...というのが己の結論。(自分でチューンできる技量のないものとしては)
てな具合に弄ってみたはいいけれど、スペック低いし騒音がつらいので、箱だけ残して中身(電源やM/B)は手持ちの余りパーツで置き換えよう ... と思ったのだが、
そこはメーカー製パソコン、一見汎用パーツのようで、そのままじゃあ換装できない要素が細々あり、結局、粗大ゴミになりそうな結末。
2013-9-22[日] PCKeyboardHack, KeyRemap4MacBook でのダイヤモンドカーソル(^QF 有)設定バッテリー妊娠で放置してた macbook を復活中(バッテリー新調)。実質win7機としてしか使ってなかったのだけど今度こそmac(osx)をそれなりにさわろうかと(未だにmacよくわからん)。macbookは昔買ったlate 2008 な奴なので今更すぎますが新しいの買うまでには至らず。メモリ8G,ssd250G にしたのでまだなんとかなるかな、と。osx10.8 や win8 (win7入れ直してwin8upgrade) にしたはいいけど、まず、何より辛いのはキーボード。普段PCのテンキーレスキーボード(フルキーボードからテンキーだけ無くしたタイプ)常用、それがないならダイヤモンドカーソル(VZ系)配置必需、という自分でも少し困った生態ですが抜けられず、で、windows環境については自前ソフトで対処だけど mac 側はさて... といっても mac 側の選択はやっぱり PCKeyboardHack, KeyRemap4MacBook のインストールがベストの模様。以前だって入れてましたが、その能力を発揮できていなかったようで(ソフトが発達した面もありそうだけど)、今回本気で設定してみたら、ダイヤモンドカーソルで^QFとかの 2 ストロークキーも使えて、かなり幸せでした。 ということで、以下、PCKeyboardHack, KeyRemap4MacBook の設定メモ. ※[追記] PCKeyboardHack は OS X 10.8 用以降は"Seil"に、KeyRemap4MacBook は OS X 10.9用以降は "Karabiner" に変名された模様. ※[追記] ( こっち ) で 窓使いの憂鬱 を使ってlinuxのダイヤモンドカーソル対応してみた 日本語キーボードを接続する場合
PCKeyboardHack で、For Japanege の 3つをチェックする(Enable NFER Key on PC keyboard. Enable XFER Key on PC keyboard, Enable KAATAKANA Key on PC keyboard). ※ 無変換キー=IME off 変換キー=IME on なので 全角/半角キーは使うこと無く. 無理に全角/半角キーを使うことは考えず、むしろ、このほうが楽なので、windows 環境も 無変換を IME off にすることにした.
ダイヤモンドカーソル関係
実は KeyRemap4MacBook の Change Key の項目に Diamond Cursor 等各種変更があるので、好みのがあればそれを使うのが吉... だけど好みにあうのがなかったので自分で private.xml を書くことに. やりたいことは、他の修飾キーによる動作を壊さずに、別途CapsLockキー同時押しでダイヤモンドカーソル移動等VZ系の基本操作すること(簡易に実現できる範囲のみ用意)
まず CapsLockキーを別のキーにするには PCKeyboardHack で、
CapsLock については、これだけでなく、macos本来のキー設定でCapsLockを無効にする必要があるらしいのでそれも行う.
次に private.xml の作成.
設定方法は KeyRemap4MacBook の Misc & Uninstall(タブ) の Open private.xml を開けて、作ったものを上書き. Change Key(タブ) で ReloadXML して項目が現れたら必要な物をチェック.
作ってみた設定項目は、(以下ここでの ^ は右Ctrlの意)
生成するキーは標準のテキストエディタ準拠。
作成にあたってはググっていろいろ参照…ちゃんと控えてなかった。まあ 2 ストロークキー以外は難しいことはなかったです。
(追記: 2ストロークキーの設定では参考元にあるように config_only を使用。使わずオーソドックス?な方法だとたしかに "キーリピートが止まらない" 症状でした。ただ VK_CONFIG_SYNC_KEYDOWNUP_???? の使い方がよくわからずウマく設定できず、他の方の2ストロークキーの設定で使われてる VK_CONFIG_FORCE_ON/OFF を真似ねてみたところ希望の動作になりました。
※他の方の設定みてるとアプリケーション別とかの対応をされてたりして、結構高度なカスタマイズできるようですが、そこまでの気合はなく. ※ 編集キーの役割が違うので配置的にPCキーボードに拘る必要ないけれど、キータッチ感のために変える値打ちはある……が、テンキーレスキーボード、リアフォ 91U を繋ぐと右側の日本語キーボード配置な記号群が英語キーボード準拠に化ける。数字キー部分の記号は日本語準拠なので解せない。FILCOの茶軸やFujitsuの8769 を繋ぐと問題なく入力できたのでそちらを使えばいいのだけど、ちと敗北感あり。
※追記:そもそも己がダイヤモンドカーソル使うようになったキッカケが、むかーし FM-11(OS-9)とPC98(DOS)を使ってたからで、違う文化圏のマシンを触る限り二十ウン年前と問題はかわらんし解決方法もまた……なんですが、どうせ覚えるなら emacs やら vi(m) のほうが幸せだったかもな、です(今更その気はないけれど)。unix系osは生き続けemacs,vimユーザーも新規ユーザーがいるわけですが、 DOS(CP/M) 系の操作は Windows に殺されたので、未だにダイヤモンドカーソルに拘ってる人はおそらく40代付近以上……滅んだ操作体系で 小指痛くなることもあるけれど 左手のみですむ移動操作は(特に左利きには) それなりに幸せかもとは、言っておこう。 テキストエディタ物色中さて、最低限キーボードを触る気になれるようになったけれど、問題は常用できそうなテキストエディタがあるか……まだ物色中。
決定打は今のところなし。Windowsではなんだかんだで秀丸(VisualStudio風キーカスタマイズ)に慣れすぎて……全角空白可視化したいしvz由来のスタック型カット&ペースト必需だし tsv,csv モードもなるべくほしくetc…(後半わがままなのは自覚. 正直エディタを使いこなせてるかというと?な奴だし)
NxWineで秀丸 は 思いの外動いてくれて良さげだったけれど、細かい不具合はあるにはあるし、キー操作でタブ切り替えると窓のフォーカス離れるのでいちいちマウス操作することになり今は諦め。(他の商用winエミュソフト使えばマシなんだろうか?)
性能的には sublime text 2/3 がよさそうだけどシュアウェア購入に見合うかは微妙. この金額払うのなら秀丸で使ってる機能がほぼほしいと思ってしまい…(スタック型カット&ペーストはありそ?なくてもスクリプトでなんとかなりそうな気配だけど tsv,csvモードは無さそ?。アウトライン表示とかはどうだろ). win,mac,linux 用があるのは魅力だけど(ああwin版はime入力窓が追従せず離れて表示されるのは嫌状態). フリーのだと mi, TextMate2, komodo edit あたりか. mi は日本人作者だしシンプルでとりあえず使う分にはよさげ……キーカスタマイズが部分的なのでもう少し設定できればと思う(理解が足りてないだけ?) komodo edit はIDE的要素つよくてテキストエディタとしてはちょっと重め、だけどこれもオープンソースだし win/mac/linuxあるのは魅力か. TextMate2 は元々商用でそこそこ評判がよかったものがオープンソース化したものらしく高機能、だけどごちゃごちゃしてる感じ、でも日本語の解説頁はままありそう……あ今ググってこれみたらデフォルトでスタック型カット&ペーストできた。これは俄然興味わいてきた ……もうしばらく右往左往してます。
2013-6-16[日]前回の影響もあって、windowsで動くCコンパイラの機能を比較した頁、
を書いてみた。ざっくり kr,c90,c99,c11 およびvc・gcc拡張の機能を試してる。
2013-5-26[日] c++03コンパイラの機能実装の比較昔 ow や dmc のc++の(文法)実装状態をチェックしてる表をみたことあったなあ、と探してみれば、すぐみつかる. http://cmeerw.org/prog/freecpp/ 最終更新が2006で、それ以前のコンパイラの比較だけれど、チェックプログラムのソースが公開されていたので、ow 含むそれらよりも新しい(ヴァージョンの)コンパイラで試してみた。 もとより網羅されてるわけでないし今時のc++11世代のコンパイラの比較としては不十分だけど(SFINAEのチェックもなさげ)、owやdmcが以前よりよくなっているかを見る分には、と。
bcc 5.5.1 を表に加えるんじゃなかった...面倒増えてしまった(いや本当はpassしてるのもひと通りみたほうがよいくらいだけど)
2013-5-21[火] open watcom v2年始くらいに某2chスレで 年末に2.0でるかも(ソースはニューズグループ)、とか書かれていて、けれど、watcom のサイトのデイリービルドみてもちっとも進んでなさそうで、ほんとかしら、と首をかしげてたのですが、今日、検索しててみかけました。 https://github.com/open-watcom どうやら open watcom v2 を非公式にフォークしてたらしい。 リポジトリ覗くと、24時間以内の更新とかあって、そこそこ活動してそうな雰囲気。 といってもスタッフ的にお二人の名が書かれていたのでこじんまりとしたものなんだろうけど。 v2 ってことは Version 2 todo list の内容を目指しているのでしょうかね。どうなんだろ。watcom c++ の実装自体は c で書かれていて結構大変そうにも思いますが... まあちょっと楽しみです。
2013-5-20[月] stlport 5.2.1 for dmc,Open Watcom今更ながら(現実逃避がてら) stlport 5.2.1 を Open Watcom 1.9 や dmc 8.56 に対応してみてた。ow のほうはそれなりになった気もするが dm のほうはリンカのこともあっていまいちかも。(何か根本的に見逃しているような気もする) 前回 stlport いじってたのが3年前……時間立つのは速いなあ。5.2.1がrelease最新版のままで開発自体は進んでなくもないけれど、vcでさえ面倒臭そうだったのと、watcom への対応を思うと古いコンパイラへの対応が残っているほうが無難そうに思えて結局5.2.1。(追記: 最近の開発版のほうはどうもunix系でc++11対応のg++のみなのかも. vcはおろかmingwもだめそう) 面倒くさいといえば付属のbuild環境も面倒で無視した。手抜きでバッチでコンパイル。ow,dmc以外にコンパイル試したのは vc(7.1,9,11), mingw g++4.7.1tdm版(32bit/64bit), bcc5.5.1。(ああ mingw 64bit対応が一番マトモな作業かもしれない……動作確認不十分だけど) 作業の参考にと pointer_specialization.txt と 「コンフィグレーション マニュアル」 をいつものごとく翻訳サイトの訳を手直ししたりしてみたが……コンフィグは内容古くて実際には結構廃止されてたり追加されてたりするようで少し徒労感有り。
と、ま、モノは stlport521dmow2.zip。
(txt)
2013-5-19[日] optlink のコンパイル仕事が一段落したので放置していた諸々に手をつけねば、だけど、忙しい最中の現実逃避の残滓を先に…とグダグダ。現実逃避で dmc や ow で遊んでたのですが、dmc ってc++としてはowよりよさげだけどリンク時にハングしたり-gでデバッグ付でまともに動かせなかったりと optlink がかなりネック。交代のリンカ探すもデバッグ機能対応してるやつなんてそうそうなく…で灯台下暗し。optlink 自体がオープンソース化されてたようで dm856c よりも新しいソースがコミットされてた。コンパイルして使うとハングとか -g の具合が改善されてる模様。(といっても -g付きでちょっと大きいリンクすると Error 168: >64K Global Types の刑なのは変わりませんが)
とりあえず、コンパイルメモ
コンパイルには dmc と vc(9) が必要。(express版 でいけるかは未確認)。
vc の nmake と ml(マクロアセンブラ) あたりを使っているよう。 dmcとvcのパスを通し(vcvars32.bat 実行後 set path=x:\dm\bin;%path% をするなり)、 imagecfg.exe をパスの通ったところに置いておく.(不精して dm\bin に掘り込んだ)
ダウンロードしたzip を \dm があるドライブの適当なフォルダに解凍。
(gitで取得のほうが普通か?). 一応 開発者と同じ状態にするならば
その中の ができているので、dm\bin に上書きコピー。
追記 dmd 2.063 (及びそれに対応するdmc.zip)付属のlink.exeは2013/04 の修正が反映されたバージョンになったようで、わざわざコンパイルしなくてもよくなった。
2012-12-14[金] boost::container で俺俺アロケータこれは C++ Advent Calender 2012 14日目 の記事です。 C++11やboostをまだロクに使っていない人間なので、雰囲気を掴むために boost1.52 の boost/container フォルダを一寸覗いてみました。 new,delete等メモリーアロケート のコストを無視できないターゲットを扱うことも多いので、俺俺アロケータを渡してみたいってのもあり。 C++11仕様でアロケータが今までより楽にかけるようだし、scoped_allocator_adaptor も何か使えそうですし. ※いまだvc9をおもに使ってるので(vc12も使いますが)、C++11の記述はさけてます.
boost::containerboost::container ですが、C++11で仕様拡張された vector,list,map,set,deque 等のコンテナの機能を C++03コンパイラでもなるべく使えるようにした互換コンテナ群+α です。 C++11 で増えたコンテナ(array, unorderd_map 等)については すでにboostに実装があるためか(?) boost::container には含まれていません。詳しいことは こちら とか他のサイトにあたってください。 将来的にどんどん変わっていく部分も多々あるでしょうが、とりあえず boost_1_52_0/boost/container/ フォルダで wc してみると、41ファイル 27613 行(detailフォルダ含)。 実際には container 外のboostヘッダをいろいろinclude しているので実質はもっと大きいです(map,set(tree)等は boost::intrusive のものを使っているようです)。 (ちなみに boost_1_52_0/boost/ では 8779ファイル 約172万行、やっぱりデカいです) boost/container/ 直下にあるファイルは
(ptr_container のような)既存のstdコンテナを継承して拡張(メンバー関数追加)とかではないです。 (といっても boost::intrusive 等 boost内の他のライブラリはよく使われています)
次に、いくつかコンテナについて。
vectorざっくりメンバー変数の部分だけ抜き出してみて、
template <class Allocator> class vector_alloc_holder { struct members_holder : public Allocator { pointer m_start; size_type m_size; size_type m_capacity; } } template <class T, class Allocator> class vector : vector_alloc_holder<Allocator>; (SGI版派生等)他の実装だと全てポインタで管理していることが多いですが、この実装はsize,capacityを個数で持っています(個人的にはデバッグ時にメンバー変数で見れるので好み)。
Allocatorは、(非static)メンバー変数がない場合に実質 0バイトになるよう struct の継承元になっています。 ということで C++11なら // 1回 N個 アロケートするだけの Allocator template<typename T, unsigned N> class SampleAllocator1 { public: typedef T value_type; SampleAllocator1() { } T* allocate(unsigned n) { assert(n == N); return reinterpret_cast<T*>(buf_); } void deallocate(T*, unsigned) { } bool operator == (SampleAllocator1 const & ) const { return false; } bool operator != (SampleAllocator1 const & ) const { return true; } private: typedef double buf_t; buf_t buf_[(N*sizeof(T)+sizeof(buf_t)-1) / sizeof(buf_t)]; }; //typedef std::vector<int, SampleAllocator1<int,1024> > Int1024Vector; typedef boost::container::vector<int, SampleAllocator1<int,1024> > Int1024Vector; Int1024Vector g_intVec;
みたいな真似をしても大丈夫のはず...と書いたけど上記はたぶん boost::container に依存しています.
list実装は boost::intrusive の定義も多く面倒なので、かなり端折って (boost::container としてでなく) ありそうな list 実装のメンバーの雰囲気として以下 (すみません、後付で boost::container からめたはいいけど対処しきれず...)
class Node_Base { Node_Base* next_; Node_Base* prev_; }; template<typename T> class Node : Node_Base { T value_; }; template<typename T, class A > class List : A { ListNode_Base root_; size_t size_; };
かなりいい加減ですが、メモリーの雰囲気がわかれば...
map,multimap, set, multiset
map,set は たいてい赤黒木 実装のようで、boost::container では boost::intrusive を使ってるようです。
class Node_Base { Node_Base* left_; Node_Base* right_; Node_Base* parent_; bool balance_; }; template<typename T> class Node : Node_Base { T value_; }; template<typename T > class Tree : A { Node_Base root_; size_t size_; };
List や Map は 渡された アロケータをそのまま使うのではなくて、allocator のメンバーの template <class U> struct rebind { typedef allocator<U> other; }; を用いて、 T でなく Node<T> の allocator を使っています。 また、allocator への要求は通常 1個単位になると思います。 ということで、list や map の俺俺アロケータとしては、ノードサイズ固定のメモリーをプールしておく、というのが考えられます。
template<unsigned B, unsigned N> class SampleAlloc2 { public: SampleAlloc2() { for (unsigned i = 0; i < N-1; ++i) buf_[i].link = &buf_[i+1]; buf_[N-1].link = NULL; root_ = &buf_[0]; } void* allocate(unsigned n) { assert(n == 1 && root_); Link* p = root_; root_ = p->link; return p; } void deallocate(void* t, unsigned n) { if (t) { assert(n == 1); Link* p = reinterpret_cast<Link*>(t); p->link = root_; root_ = p; } } private: union Link { Link* link; char b[B]; }; private: Link* root_; Link buf_[N]; }; enum { ELM_SIZE = 32 }; // コンテナのノードを含んだ要素のバイト数. enum { MAX_SIZE = 16 }; template<typename T> class SampleAllocator2 : public SampleAlloc2<ELM_SIZE,MAX_SIZE> { typedef SampleAlloc2<ELM_SIZE,MAX_SIZE> base_type; public: typedef T value_type; SampleAllocator2() {BOOST_STATIC_ASSERT(sizeof(T) <= ELM_SIZE);} T* allocate(unsigned n) { return reinterpret_cast<T*>(base_type::allocate(n)); } void deallocate(T* t, unsigned n) { base_type::deallocate(t, n); } bool operator == (SampleAllocator2 const & ) const { return false; } bool operator != (SampleAllocator2 const & ) const { return true; } }; // list typedef SampleAllocator2<int> IntAllocator; typedef boost::container::list<int, IntAllocator > IntList; // map struct cstr_less { bool operator()(const char* l, const char* r) const { return strcmp(l,r) < 0; } }; typedef std::pair<char const* const,int> smpPair_t; typedef SampleAllocator2<smpPair_t> SmpPairAllocator; typedef boost::container::map<const char*, int, cstr_less, SmpPairAllocator > StrIntMap;
※ 要素(ノード)サイズ(ELM_SIZE)が直うちでみっともないですが、とりあえずは。
deque(実装面倒なのと、ターゲットであまり使わないし... いえ、時間切れ. 後日に何か)
stringstd::stringは(C++03では)ライブラリごとに結構実装がばらけています(EffectiveSTL に載ってるのとか)。 c++11 で縛りがきつくなったので多少にかよってくるかもですが、それでもいろいろできそうです。 最小限としては vector と同様の内容になりそうですが... boost::container::string から端折って抜き出してみると
struct long_t { size_type is_short : 1; size_type length : (sizeof(size_type)*CHAR_BIT - 1); size_type storage; pointer start; }; struct short_header { unsigned char is_short : 1; unsigned char length : (CHAR_BIT - 1); }; struct short_t { short_header h; value_type data[UnalignedFinalInternalBufferChars]; // UnalignedFinalInternalBufferChars ≒ (sizeof(long_t)-sizeof(short_header))/sizeof(value_type) }; union repr_t { long_raw_t r; short_t s; }; long_t は長い文字列でアロケータを用いる場合で、sizeof(long_t)-α 以内におさまる短い場合は short_t のようにして領域をバッファとして使い動的確保せずにすませる、という工夫がされています。 64bit CPUだと、vector互換でもsizeof(ポインタ)*3=24bytesになりますし、結構ありがたい実装です。 (最近作ってた 俺俺string がまさにこのタイプだった... もうboostのでよく) ※ メモ: ビットフィールドは エンディアン問わず、先に書かれたものが低アドレスに配置される.
stable_vector, flat_map, flat_set
stl コンテナの代用品でなく、vector 実装をベースにした特化版。
詳しいことは他のサイトにあたってください。
scoped_allocator_adaptor
C++11 で増えた アロケータです。
scoped_allocator_adaptor を用いれば、例えば map<string,int> のようなコンテナで、ローカルなアロケータをstringとmapで共通で使えるようにできます。 アロケータをコンテナのメンバーにするので、当然メモリー使用量も増えます(とくに内側のコンテナは数が多くなりやすく)。 ので、コンテナのメンバーにするアロケータはポインタだけにし、実装はさらにその先に用意、といった感じにすることになると思います。 詳しいことは こちら のサイトとかみてもらったほうがいいです。 正直なところ一見ではよくわからず、試してみてなんとなく納得の状態です。 以下、試してみたソース.
#include <stdio.h> #include <boost/container/map.hpp> #include <boost/container/string.hpp> #include <boost/container/scoped_allocator.hpp> #include <boost/foreach.hpp> // mapで解放しないこと前提に、スタックで解放無視の簡易アロケータ. template<unsigned N> class SampleAlloc3 { public: SampleAlloc3() : size_(0) { } void* allocate(unsigned n) { assert(size_+n <= N); void* p = ptr(size_); size_ += n; return p; } void deallocate(void*, unsigned) { /*dummy*/ } private: typedef double buf_t; void* ptr(unsigned n=0) { return (char*)buf_ + ((n + sizeof(buf_t)-1) & ~(sizeof(buf_t)-1)); } private: unsigned size_; buf_t buf_[N / sizeof(buf_t)]; }; // map<string,int> のメモリー template<unsigned SAMPLE_ALLOC3_SIZE> class Hoge { public: Hoge() : strIntMap_(std::less<String>(), MapAllocator(StrIntPairAlloc(&smpAlc3_))) {} void insert(const char* name, int val) { //strIntMap_[String(name, &smpAlc3_)] = val; strIntMap_.emplace( name, val ); } void printAll() { BOOST_FOREACH(StrIntPair p , strIntMap_) printf("%s = %d\n", p.first.c_str(), p.second); } private: typedef SampleAlloc3<SAMPLE_ALLOC3_SIZE> Alloc; template<typename T> class SampleAllocator3 { public: typedef T value_type; // SampleAllocator3() : alc_(0) {} // デフォルトコンストラクタは用意しないほうが安全. SampleAllocator3(Alloc* alc) : alc_(alc) { } template<typename U> SampleAllocator3(SampleAllocator3<U> const& r) : alc_(r.detail_get_ptr()) { } T* allocate(unsigned n) { return reinterpret_cast<T*>(alc_->allocate(n*sizeof(T))); } void deallocate(T* p, unsigned n) { alc_->deallocate(p,n*sizeof(T)); } bool operator == (SampleAllocator3 const & ) const { return false; } bool operator != (SampleAllocator3 const & ) const { return true; } Alloc* detail_get_ptr() const { return alc_; } private: Alloc* alc_; }; typedef boost::container::basic_string<char, std::char_traits<char>, SampleAllocator3<char> > String; typedef std::pair<const String, int> StrIntPair; typedef SampleAllocator3<StrIntPair> StrIntPairAlloc; typedef boost::container::scoped_allocator_adaptor< StrIntPairAlloc > MapAllocator; typedef boost::container::map<String, int, std::less<String>, MapAllocator > StrIntMap; private: SampleAlloc3<SAMPLE_ALLOC3_SIZE> smpAlc3_; StrIntMap strIntMap_; }; void sample3() { Hoge<1024> hoge; hoge.insert("xyz", 1); hoge.insert("abcdefghijklmnopqrstuvwxyz", 2); hoge.insert("1234567890", 3); hoge.printAll(); }
emplace, emplace_back、単に(コピー)コンストラクタの頻度さげるだけじゃなくて、今回のような場合まさに必須の類でした。
おわりに
遅刻したうえに、
グダグダな内容ですみません。 例に挙げたソースのとおり allocator の記述に関してはすごく楽でした。 が、boost::contaneier を std:: にかえて vc12 でコンパイルすると全く×だったので boost::contaneier に結構依存していると思います。 時間の余裕なくなってるので、たぶん後日 補足修正書くことになりそうですが... おつきあいいただきありがとうございました。
追記: コンパイル試したときのソース
[コメント]
2012-12-12[水] サイト分割このサイトのプログラミング以外のページをこっちに移した。いろいろ思惑あれど遅々として進まず、けれど年越す前には何かしとこうと... というか、c++ advent calendar に参戦申込(12/14) しちまってたのでその前に多少なりとも、で。(肝心のネタをどうしよう... )
2012-10-27[土] Win8pro インストールWin8の1200円のアップグレード版をダウンロードした勢いで(サブ機だし)Win7引き継ぎでインストールしたはいいけれど不安定になって結局更地で再インストール中。(OSのインストールは楽ちんになっても各アプリ側が面倒この上なく) 引き継ぎインストール、最初はそこそこいい感じに移行できたように思ったけれど、使いだすと引き継げてるもの・引き継げてないものがあってチグハグだった。
特にWindowsエクスペリエンス計測の このコンピューターの評価 がエラーで動かなかったときは唖然... [追記] Win8 設定完了前に メインでつかってる Win7機がお亡くなりになったため win8に強制移住。HDDが生きてたので被害は少ないけれど、元環境のアプリを起動できないのはやっぱり面倒。 [追記2] iPad mini を購入したら一気に win8 熱が冷めてしまった。片手で持ててそこそこ広いってのは思いのほか重要だった模様。寝ころんでpdf読んで寝落ちするのはある種幸せです。
2012-6-3[日] LaVieLight に Win8rp をインストール久しぶりにサブノートPCを持ち歩こうと思いたち、Win8cp を試しに入れて放置しっぱなしだった LaVie Light BL350/D を復活中。 丁度 Win8 Release Preview が公開したてだったので更地にしてインストールすることにした。 マシンのほうは Atom な netbook の範疇だけど、1366x768 ドットある奴なんで一応 Metoro も大丈夫(なので Cp 版インストールできてた。非力な Atom だけど 1.5GHz 2コア(HT x4) だし、メモリは2GBにHDDはSSD120GBに換装済みなので、それなりに使える感じ)
インストール前に、まず、Microsoft アカウント(Live ID)の氏名欄を 英数のみに変更。 Win8 はPCのユーザー設定で MSなクラウドサービスと連動前提の設定と、そうでない(従来タイプの)設定を選べるが、CP 版時に いろいろ試したく 連動のほうを選んだら、Microsoftアカウント情報に登録してある氏名の“名”の文字列でユーザーが作られてしまったのだった。本名を漢字で設定したままだったから c:/users/本名 な漢字フォルダが作られしまい、古いツールを使う機会もあるプログラマとしては腹立たしい状態だったので、今回はアカウント情報のほうを変えておくことで対処。(CP版のときはこの段階でかなり萎えて余り使わず放置) 更地からなので ISOイメージをDVDに焼いて USB-DVD でインストールした。 途中でプロダクトID を聞かれたので、Webインストール版を実行して手順に従って操作している途中で表示されたプロダクトID を用いた。(この方法でよかったのか不明だがとりあえず動いてる)。 当然インストール開始時には以前インストールしたwindowsの情報は引き継がないほうを選んだのだけど、インストール後デスクトップを選んでみれば壁紙無し単色でタスクバーが縦置き状態……これWin8のデフォじゃないよね? CP版で設定していた己の設定ぽいので、アカウントに紐付された情報が一部あるのかも。
とまOS自体は結構すんなりインストールできたと思う
その他アプリのインストールについては、CP版より更にスタートボタン破棄の方向が強められてる感じ。 CP版のときは、タスクバーに新規ツールバーとして C:\users\(ユーザー名)\AppData\Roming\Microsoft\Windows\Start Menu\Programs を登録すれば簡易なプログラムメニューとして使うことができたけれど、RPではアプリをインストールしても そもそも このフォルダにアプリのショートカット等は作られず、Metoroなスタート画面にドバっとアイコンが現れる状態。元の(start-プログラムメニューでの)フォルダ構成無視なんで一気にスタート画面がゴミ溜めと化す。全くスタート画面を使わなくてすむならそれでもいいけれど、立ち上げ時に必ず来るしね……しかたないので、右クリックで不要なものをマークして一気にピン止めを外した(ちょっと面倒)。 ※ 全部が全部じゃなくて一部(susie)は従来のメニューに現れるものもあった(たぶん START-プログラム・メニューへの登録が今のwinの作法に則っていないのかも)
インストールしたプログラムでまともに動かなかったのは TortoiseSVN(1.7.7)。
チェックアウトしたりTortoiseSVNのメニューが閉じられるときにクラッシュする。
それだけでなく Exproler(タスクバー) が不安定になって ゴミ箱を空にしようとしたり
タスクバーを自動に隠す設定をやめようとしたらハング(即再起動するが)。 とりあえず TortoiseSVNをアンイストールして再起動で元に戻ったけれど、最初 アンイストール後シャットダウンで終了して電源ボタンで起動しなおしたら OS不安定のままだったので結構あせった。2,3回やっても変わらずで。再起動を選んだところ"更新します"みたいなメッセージでて再起動後ゴミ箱空にしてもハングしなかったので一安心だけど。Win8からの高速なブートや終了のための仕組みの影響なんでしょうかね。 追記: またゴミ箱空にしたらハングするようになった。FireFox(12)も終了するたびにクラッシュレポータが表示される。 どうやら今度は Google日本語入力が原因だった模様。設定をMS IMEに切り替えたらゴミ箱空にできたしFireFoxも正常終了、googleIMEに切替るとまたハング...うーシステム拡張的なものは結構引っかかる感じなのかな。
2012-2-29[水] pyukiwiki 0.2.0月末に作業締め日がある場合、さて閏年でよかったと思うべきか... はどうでもよく。 このサイトに使っている pyukiwiki 共同開発版は一時開発が止まってたけどまた再開していたようで v0.2.0p1 が出てたので更新してみた。 wikiプログラムの更新だけでサイト自体のリニューアルというわけではないけれど。でも微妙な修正をちょこちょこ、どうせspamしか書き込まないのだからcommentは無しにしたり(少し寂)、無理にblog風にすることもないなで昔のtop頁に戻したり。 (もう少しいじってからとか思うもその状態で2週間放置だったり。) pyukikwikiの更新のほうは、久しぶりすぎて、いろいろ細かいこと忘れていてすごく手間取った。自分の改造やプラグインの事情が記憶の彼方なのは当然として、使用した環境じゃ CRLF だとcgi(perl)が動いてくれないことに中々気づかなかったり(本来なら年始に移行するつもりが) 0.1.9あたりから utf-8 専用板が用意されてるようなので、utf-8 版にしようと思ったのも一手間。頁等のutf8へのコンバータプラグインが用意されているけれど、さすがに 0.1.7では駄目のようで一旦 euc(従来)版 の0.2.0環境に移行する必要があり... コンバートだけだからと compact 版を使ったところ動かなかったり(full版は動いてくれた) 改造|自前のpluginの辻褄合わせは微妙に面倒だけどしかたなし。editは改造しなくてもよくなってたけど、calender2とか改造に依存したサイトになってるので... 入れ替える時面倒なので結局 改造プラグインは名前を変えて対処したり。 スキンは 0.1.7で使ってたのがほぼ無変更で使えたのでそのまま. うち向きの仕様も増えてるっぽいので ほんとは 0.2.0 のをベースに作りなおしたほうがいいのだろうけど。
と、pyukiwiki共同開発サイト 見に行ったら 0.2.0p2 のアナウンスが出てた模様。 compact版はやっぱり配布内容がおかしかった模様. (p2にしようかと思うもダウンロードは3/1予定... とりあえずp1のままで)
2011-11-11[金] C++11 を忘れたい、3年くらい
ポッキーの日、と言われてすぐになんのことか思え出せなかった虚け者です...いや、覚えていなくてもいいと思うけれど おすそ分けのポッキー食べたんだからちょっとくらいは覚えておこう>己。で連鎖的に C++11 を思い出す、けど、まともな処理系がでてくる2,3年後までは知らずにすませていたかったというか忘れたい。今 C++ 03 で(ライブラリを)作るものにとっては非常に目の毒です。(と、ま、ただの生存証明)
2011-1-6[木] stlport.orgひさしぶりに www.stlport.org を見に行ったら消失していて焦ってしまう。 といっても sorceforge にも頁があるので… というより http://stlport.sourceforge.net/Home.shtml が公式ということか? 左サイドのメニューをちゃんと認識していなかった。 いまさらながらhistoryやFAQを見てなんとなく納得。 (放置してた旧サイトのドメインが消失したってだけのことでいいのかな) あとFAQみて4.6系はいまいちな状態でv4の最後の綺麗な版が4.5.3だったことを知る。dmc用公式 stlport が4.5.3どまりなのはそのへんもあるのかと連想。 現在の最終の公式リリースは 5.2.1(2008) のままだけど、開発版のソースをみると、メジャーバージョン(#define)は 6 になっていた(6に向けての開発中) C++0xを考慮してるし、あまりに古い仕様のコンパイラは対象外にしていってるよう。 さすがに、メンバー関数テンプレートのサポートは必須扱い、stdint.hも利用してるしで、互換のためにごちゃごちゃしていた記述が省かれたり単純になったりですっきりしてきている。(v4→v5 時ほどではないのだろうけれど)
ライセンスについては LICENSE ファイルが同梱されているので、それを確認、と。
[1/8追記] どうも http://stlport.com/ がある模様。消失じゃなくて移転ということなのか?
2011-1-1[土]あけましておめでとうございます。今年もよろしく。 去年は人生の節目のようなことがあった反面、 総じてグダグダな一年だったので来年はもっと頑張る予定。 いや今年はも少し頑張るです(数え年は気になる)
また、そこそこ環境整い、さて年賀状印刷しようとしたら、今度はプリンタ(Canon MP640)がエラー6C10 で動作できず。検索でみつかるモノを見ると、基本、メーカー修理するしかなさそうだ。年末年始修理受け付けてないようなので後日。しかしまだ半年経ってなくてあまり使ってないのに…いやプリンタは使用頻度が低すぎるのがまずいのだろうけど。ああ6C10で引っかかる頁はMP640以外もあるのでCanonの機種はエラーコードを継続しているのだなあ、と当たり前といえばそうだけれど妙に感心してしまった。
2010-3-21[日] LLVM lli での実行そういや LLVM の VM(lli) ってどれくらいなんだろうか、とちょっと気になったので 3/12のを lli で実行してみた。 llvm-gccでコンパイルして、 llc で生成したネイティブアセンブラソースからexeを作ったもの、と、 lli で実行したもの。 llcからのexe結果は不精して、前回のからコピペ。 今回試した lli はそのときより数日新しいllvmになっていたり 多少測定環境がぶれていたりするけれど、おおざっぱな雰囲気をみる分には...で。 (ソース&exe)
大筋 lliの実行結果は llc でネイティブアセンブラ生成した場合と同様の結果のよう… 仕組みからすれば、lliのほうは測定範囲外の部分で余分に時間かかってる (実行時コンパイルしてる)だろうが、 .bcサイズに対する今時の実行環境のパワーを思えば たいしてペナルティがあるわけでなく、ってとこ、か? ( ロード時に一括コンパイルしているのかな?) ネイティブ実行以外興味なくて、最初にちょろっと試しただけで、以外に速いかも なんて思ってたのだけど、以外に、どころじゃ、なかった。 LLVM関係の記事(appleのopenglな件とか)に書かれていることを思い返せば、 こういうことなんだろうけど、実感してなかった、というか、わかってなかったことを実感です。
2010-3-20[土] LLVMで Cに変換してみるLLVM (llc) には Cソースコードを生成する機能があり、 C++ソースをCソースにすることができる模様。 ただし C++ から C への直接変換するのではなく、 一旦.ll/.bc(llvm用アセンブラソース,ビットコード)にしたものを、 llc.exe で(ターゲットCPU用アセンブラソースのかわりに) Cソースへ変換するというもの。 普通のアセンブラソースをCに変換した場合 オーバーヘッドは当然あり、 全体として問題になることはそうないかもだが (昔アセンブラソースからCに変換しての移植作業を何本かやったときの印象)、 何割(何倍)か遅くなっていてもおかしくない。 が llvmの .ll/.bc は、 普通のアセンブラソース/オブジェファイルでなく、 それより情報の多い中間コードなわけだし、 llvm-gcc & llc でオプティマイズしているため、 Cソースになっても元よりも速くなる可能性もありそう。 ということで、ちょっと、試してみた。 3/12にしたテストの1つ目。 C++でなく Cソースだけど。 (32bit色640x480→1920x1080拡大の50回生成の平均時間を求める奴. 実行環境は前回同様) llvm-gcc で llvm-gcc -S -o tmp.ll -emit-llvm -O2 -DNDEBUG test.c llvm-as -f -o tmp.bc tmp.ll llc -march=c -f -o test_dst.c tmp.bc
として test_dst.c を生成。
ヘッダ込みでコンパイルされた結果を C化してるのだから、
とりあえずstderrやwin-apiを使わないようにして(外部はC標準関数だけにして)回避。 ついでに 一旦 ldでまとめたものを.llに戻してから llc したものも試す。 llvm-gcc -S -o tmp.ll -emit-llvm -O2 -DNDEBUG test.c llvm-as -f -o tmp.ll.bc tmp.ll llvm-ld -o tmp.ll.exe tmp.ll.bc llvm-dis -f -o tmp.2.ll tmp.ll.exe.bc llvm-as -f -o tmp.2.bc tmp.2.ll llc -march=c -f -o test_dst.c tmp.2.bc 変換後のソースは、 これ。 前者の変換後ソースには元の関数を維持しているが、 後者の変換後ソースでは関数はmain()だけになっている。
が失敗したモノもあり、 実行できたものについての結果は以下 (単位:ミリ秒)
測定したタイミングによっては10,20ぐらいの差がでたりするかもで、 ざっくりとした感じに受け止めてもらうとして。 LLVMのツールであるllvm-gccではあまり差はないようだが、 他のコンパイラでの結果は、多少なりとも llc で変換したソースのほうが速くなってる雰囲気。 ただ前者と後者の変換については、モノによって多少違うも、 測定誤差でありえる範囲の差で、あまり違いはなさそうに思う (関数の分割具合の違いによるオプティマイズの トレードオフはあるだろうで、それが微妙に出てる可能性もあるけれど) 他のソースも試すべきたんだろうが、面倒で挫折... 他の例でも速くなるのか遅くなるのかどうかは結局ソースしだいと思うが、 それでも元のソースよりも速くなる例ができたわけだから (元ソースがヘボいのだろう、というのは置いといて)、 他の例でもそう酷く遅くなるようなことはないんじゃないかと期待.
C++ → C
で、 肝心の c++ → c . が、g++(clang)側のライブラリ(.a)に実体がある関数や機能を使った場合は、 コンパイルできても当然リンクが行えない状態。 bad_alloc みたいなライブラリものもそうだけれど、 virtual使ったり 例外を使った場合もそう。その他諸々。 C++らしい組み方をしてるとまずその手のものは使われてるだろうで... 足りてないリンク物の代用品をターゲット環境用にも用意できれば なんとかなるかもだが、大仕事だろう。(あるいはターゲット対応をllcに施すとか... どちらにしろ個人がチョロですむ作業ではなく)
I/Oを含まずtemplateを使ったような処理をベターCの範囲で書いてCに持ってくる、くらいなら...と思うも、やっぱ面倒だなあ.
あと、llc は c だけじゃなく c++ ソースも生成できる。
なんかすごい、というか、目が点になる、というか。
※と、まあ、発展途上のllvmを使っての感想なんで今後どうなっていくか、わからないけれど
他のC++ → C トランスレータついでのメモ。 元祖のC++実装の cfront は、Cソースへのトランスレータとして実装されている。 例外が実装されずに開発は終わっているが、 こちら でR3.0.3等いくつかのバージョンのソースが公開されている模様。 (使えるかどうかはしらない) 現行のC++をサポートしているだろうトランスレータとしては Comeau C/C++。 有料だけど低価格($50)。 面倒がって未購入だけど。 他環境向けのCソース生成ならこちらのほうがよい?(夢見てるかも)... といっても、ターゲット環境ごとの個別対応は必要だろうで、 Comeauのサイトで対応と書かれているのは比較的メジャーな環境のみのよう。
2010-3-15[月] watcom,dmcのテンプレート不具合open watcom 1.9rc1 が出ている模様. が c++ 関係はあまり進展していないのか、 1.8よりちょっとは増えたようだけれどまだ未実装のSTL関係は多そうで (stable_sortなかったり)、 クラステンプレートのメンバー関数テンプレートをクラス外に書くのも まだ未サポートのよう.
template<typename T> class Bar { public: template<typename U> void test(T* t, U bgn, U end); }; template<typename T> template<typename U> // この行. void Bar<T>::test(T* dst, U start, U last) { while (start != last) *dst++ = *start++; } のBar<T>::testの2つ目のtemplateでシンタックスエラー.
template<typename T > class Foo { public: template<typename U> void test(T* t, U bgn, U end) { while (bgn != end) *t++ = *bgn++; } }; のようにクラス内に書くのはok. ついでに、上記は dmd(8.51) でもコンパイルエラー(startが未定義)になる。 こっちは、 テンプレートな引数の名前が Bar::test()宣言と定義で違うのが気に入らないらしい。 定義側の引数 start, last を bgn, end に修正すれば ok。 (テンプレートでない引数は名前が宣言と定義で違っても無問題) 別のコンパイラ(bcc 5.? だったか)でも、 template<typename CharT, class Traits> class Foo { のようにクラス定義してメンバー関数を外に template<typename C, class T> void Foo<C,T>::hoge(…) { みたいにテンプレート引数名を変えていたら 某かの不具合がでた覚えがあり(名前を統一したらok) 汎用性を思うならば(古いコンパイラ気にするなら)、 クラステンプレートのメンバー(関数)は、 下手にクラス定義から分離せずに、内部に書いてしまうほうが 余計なバグに出会わずに済むのかも。 (分離する場合は名前を揃える... てか、ひょっとして普通揃えないとダメなのかな?) (ごちゃごちゃする定義はクラス外部に追い出して、 クラス宣言は身軽にしておこう、 なんて考えてたらドツボにハマることもあった、と)
2010-3-14[日] winの LLVM-Clang でコンパイル.
Clangで、なんとか、コンパイルできた。
(前日冒頭のつづき) コンパイル出来た llvm,Clang のソースは llvm rev.98447、clang rev.98448のもの(結構頻繁に更新されてるよう)で、vs2008sp1 にて release の ALL_BUILD して生成. まず、_WIN32等が設定されてたのは まさに Clangのソース内で設定されていた。 win環境なら _WIN32 定義してて、また、VC環境だと VCのinclude等のパス設定するような処理があった. (VCでコンパイルしたから呼ばれるのかVCがinstall済みだからよばれるのかは よくわかってないけど). でもってmingw関係も定義されてる雰囲気だった(mingwやgccを詐称してるよう).
VC関係は邪魔なので -nostdinc 指定してデフォルトのincludeパスを見に行かないようにし、
引数で llvm-gcc-4.2 の環境を流用して設定するようにした。
(別に llvmのでなく普通の(最近のバージョンの)mingwの環境でも大丈夫そう)
といった感じにコンパイル. +こまごま修正.
ヘマして何か設定が足りてないだけかもしれないが、まだ、ちょっと面倒な状況かも、で。 テストの実行結果は以下の通り。 (最適化オプションは-O2にしたけど妥当かどうかは未考慮)
他の結果はこっちみて...
追記: やっぱりマ抜けていた。--emit-llvm を指定しないとただのgccかも. --emit-llvm でllvmなコードを一旦はいてからx86向けを作る、みたい. (コンパイル手順はこっちに, 結果はこっちに追記)
で、clang も -emit-llvm 付で生成すれば同様にさらにオプティマイズできるよう。
2010-3-13[土] win標準と衝突するunix系コマンド.こちらでclangのvcでのコンパイルを紹介されてたので、やってみた。出来たコンパイラを使うとincludeでvc側の ヘッダ読み込まれてエラー。 _WIN32が定義されてるし、何か、しなきゃいけない (環境)設定が抜けてるんだろうなあ。 (includeしてないソースのコンパイルでexeやasmはつくれてた) で、そのとき、こちらの GnuWin32 をインストール. cygwinやmsys同様 unix系フォルダ構成を持つ状態のコマンドラインツール群で、 dllや言語ファイルがあるので単体コマンドを流用したい身には一長一短だけど、 コンパイラやスクリプト言語等は含まれていないので、ある意味、身軽で、 通常(常用)の環境変数PATHの最後にGnuWin32/binを追加して使うのでも よいように思えてきた。 unix系ツールと win(dos)標準コマンド名でいくつか衝突するけど、Windows/System32/*.exeとgnuwin32/bin/*.exeを比べると
ぐらいか。
当然他のパスも通るので衝突がありえるけど.
(コンパイラ関係だと link.exe とか)
※と書いたが、別に GnuWin32 でなくmsys でもほぼ同様かもだけど... (cygwinはともかく msys+mingw環境も特殊性があるのかどうか)
2010-3-12[金] ちょっと速度比較前回触った LLVM-gccやpcc,coins (で作ったプログラム)の速度が気になったので、 手持ちのCコンパイラ(vc意外はフリーのもの)で適当に実行速度を計ってみた。
(3/14修正: cygwinの計測が間違って別のmingwでの結果になってました). 試したコンパイラは全てwin環境用で
正直、オプションが、どの適度適切かは不明。
Watcomはつけすぎてるけど、基本はデフォルトに近い状態。 試したテストプログラムは
download:上記のソースのzip
たくさんやるのも面倒なので、これだけ(これだけでも結構しんどく)。 自分が大雑把に雰囲気をつかむため、なので、あまり適切でないサンプルかも。 1core 1threadな簡易な処理だけだし。 Dhrystone,Whetstoneは一応してみたけど、あまりピンときていない (処理内容わかってないからだろうけど).
実行環境は vista64(PhenomIIx4 3G)、通常のデスクトップでDOS窓とエディタ程度だが、サービスや常駐モノはある程度残った状態で実行.(テスト中の 1core がフル稼働で、あとの処理は他のcoreが大筋まかなってるだろうと期待. 以前別の計測にてsafemodeで起動して計測したほうが安定していた感じはあったけれど、酷すぎるほどの差もなかったので楽してる) 実行結果 は (3/14 一部計測し直し. cygwin計測ミス、安易インタプリタのclang対処反映)
たったこれだけだし、
測定(実行)環境はザルで実行毎の誤差からすれば
細かい数値みちゃうとまずいだろう。
特に浮動少数演算関係のオプション設定が適切でないだろうで
設定かえたら(Whetstoneとか)結果変わってきそうな気もする.
(c標準ライブラリも計測範囲で使われてるので
それの影響もどう据えるかにもよるし)。
が、おおざっぱに、vcが1番よさそうで次gcc系、
後は混戦で、ケツがTinyCCって感じか。 vc9sp1 64 の結果は CPUが64bit CPUなので、コンパイラの比較ってよりCPUの比較になるけど、 64ビットOS用にコンパイルするだけで、 結構違いがでる処理もある、という例になってるかも、で(代わり映えしない処理もあるけど)。
バイリニア拡大の結果は、
64ビット整数の利用頻度が少なかろうと
感想としては llvm(-gcc)は(いろいろミスして勘違いしまくりだったけれど)ものによっては非常に速くなったりでびっくりで早く使い易くなってほしく、 Clangはllvmをしなくても(?)これだけの性能あっていろいろ期待、 PCC は速度は思ってたよりはいいかも、だけど(Win版は)思ったより安定してなさそう、 PellesC はそんなもんだろう、 TinyCC は実行時速度じゃなくてコンパイル速度優先で(-benchなんてオプションがあるくらいだし)スクリプト言語的にCソースを扱うような方向があるようなので 実行時速度はこの程度の差ならokなんだろう、 COINSは悪くないのだけど期待しすぎてたため (ひょっとするとあまりx86系向きじゃないのかもだけど) ちょっと哀しい気分、てとこか。
※追記:cygwin-gcc、測定ミスでやりなおしたけど、gcc3.4の範囲なのでたいして値はかわってない(でもWhetstoneは結構ちがうか。浮動小数点はオプション設定しだいだろうでデフォルトがいい状態だったのかも)。 もともとcygwin使ったCOINSの結果と比較するのにmingwのでいいか微妙な気もして追加したものなので(ただの計算だけだとそう違わないだろうけど). あと最初の4つのテストで cygwin-gccとCOINSは win-apiでなくtime.hを用いて計測しているため若干条件が違う、とも. ※追記:安易インタプリタの結果は、clang対策での修正前にくらべ、どれも4,5秒短くなっている. isalphaやtoupperを手抜きなマクロに置き換えただけだが、結構ライブラリのオーバーヘッドがあったということか. テーブル参照より単純比較のほうが速いのかスレッド関係やロケール対策がらみなのかはしらないけれど。
※追記:
llvm-gcc のやり直しにおいて、Bilinear拡大は最適化されすぎて計算がなくなってしまったため、そうならないよう、若干ソースを修正(計測時間外での対処)した.
※追記:llvm-gcc 同様に clangも -emit-llvm で生成すると速くなる模様
(例えばbilinear拡大のは 100.9 に).
コンパイル不具合のメモ
(とかいたけど、coinsはちゃんと環境できていない可能性もあり)
2010-3-9[火] マイナーなCコンパイラをちょっと触るフリーでwin環境用exeが作れる、ちょっとマイナーな Cコンパイラをちょろっと触ってみた、というかインストールしてみた時のメモ. 試したのは LLVM-gcc, PCC, LCC, TinyCC, COINS。 (マイナーというと語弊のあるモノもあるけど win環境用のcコンパイラとしては、で)
LLVM-gcc(※追記:LLVM-gccでllvmを使う場合-emit-llvmオプションとつけないとダメだったよう) LLVM&clang は、 そのvmな仕組みとか、 オプティマイズ等の実力とか、 使用用途(appleの動向)とか、 GPL(v3)より緩いライセンスがらみとか、 いろいろ話題で今後の発展に期待大なコンパイラ(vm)、 だけど、 win環境で試すには、 mingw-msys 環境に追加する方法になっていて、 ちょっとまだ敷居が高い感じ。 また、残念ながら win 環境向けにはまだ clang バイナリは無いようなので、試せるのは gccフロントエンドのモノのみ。 (追記:clangのほうは自分でコンパイルすれば使える..ということでさらに敷居が高い) ただ 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同様素直な感じなのかな、と。
追記: llvmとしてコンパイルするにはオプションが必要のよう. でもってコンパイラ環境がうまく設定されていないのか、一度にコンパイルできず.こちら等を参考にして
llvm-gcc -O2 -DNDEBUG --emit-llvm -S test.c llvm-as test.s llc test.s.bc llvm-gcc -o test.exe test.s.s や
llvm-gcc -DNDEBUG -O2 --emit-llvm -S test.c llvm-as -o tmp.bc test.s llvm-ld -o tmp.2.exe tmp.bc llc tmp.2.exe.bc llvm-gcc -o test.exe tmp.2.exe.s
て感じにしてみる(後者のほうが全体に対するオプティマイズが効くかも) ※ llc で -march=x86 を付けるとwin用でなくなるみたいでマズイのだった. ※追記 こっちでLLVM-Clangを使ってみた。
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-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]
|