Archive for the ‘プログラミング’ category

WindowsのVisualStudioでpriority_queueに大量データを突っ込むと遅くなった

2月 11th, 2010

という問題が職場であったのでそのメモ。Releaseモードだとすぐ処理出来るのにDebugモードだと無限ループに陥ったかのような挙動を示す質問をもらったので調べてみました。priority_queueというのはある値に戻づいて順序が保たれるqueueです。便利ですね。

再現コードはこれでOK、C++編(標準ライブラリ) 第7章 priority_queueを参考にさせて頂きました。

#include
#include
using namespace std;

int main()
{
    priority_queue qu;

    // 要素を追加
    for(int i=0; i<100000; i++){
        qu.push( i );
    }

    // 先頭要素を取り出して出力
    cout << qu.top() << endl;

    return 0;
}

VisualStudio2005で試したところDebugモードだと動いてはいるのですが、とにかく時間がかかります。途中で処理を止めたところpushに時間がかかっているようです。そこでpushの先を追って行くと下記のようなコードが見つかります。

_Vector_iterator()
       {       // construct with null vector pointer
       }

#if _HAS_ITERATOR_DEBUGGING
_Vector_iterator(pointer _Ptr, const _Container_base *_Pvector)
       : _Mybase(_Ptr, _Pvector)
       {       // construct with pointer _Ptr
       }

#elif _SECURE_SCL
_Vector_iterator(pointer _Ptr, const _Container_base *_Pvector)
       : _Mybase(_Ptr, _Pvector)
       {       // construct with pointer _Ptr
       }

#else
_Vector_iterator(pointer _Ptr)
       : _Mybase(_Ptr)
       {       // construct with pointer _Ptr
       }
#endif /* _HAS_ITERATOR_DEBUGGING */

これを見るとdefineの定義状況によって、毎回値の妥当性チェックが呼ばれているようです。
これが過度にパフォーマンスが落ちていた原因でしょう。

_HAS_ITERATOR_DEBUGGINGはデバッグビルドでのみ有効になるようですが、_SECURE_SCLはリリースビルドでも残るようです。何用の定義なのかは軽くググッた限りではわかりませんでした。2007-07-04 – 新言語 Xtalを作る日記が見つかったぐらいです。

回避策としては両defineを0に設定すれば動かなくなり、通常のRelease/Debugの時間差で収まるようになります。

公知のラベリング処理アルゴリズム

2月 6th, 2010

ラベリング処理というのは入力画像に対して、連結する画素(同じ色とか、同じ領域とか)ごとに同じ番号を割り振る処理のことです。領域別に処理する際の前処理に使ったり、画像中の微小領域のサイズを測定してノイズ除去に使ったりと色々と有用です。アルゴリズム入門 : 第 3 章 画像処理入門 1を読むとイメージが湧くかと思います。
ラベリング処理のイメージ図

ラベリング処理は1970年代から知られている処理ですが、高速なアルゴリズムが意外と知られていません。ネットで検索すると大学の授業での説明資料が見つかる程度で、文献をあたっても解説があまりにも少なく多くは役に立ちません。

古い本でラベリング処理のアルゴリズムが詳しく説明されたものを見つけたので多くの人に有用と思い、ここに書いておきます。参考にした本は近代科学社発行の長尾真さんによる「デジタル画像処理」で、1978年に発行されています。ラベリング処理の紹介は360ページ〜361ページに有ります。

1:値を隣接する画素に伝搬させる

Sの成分をラベル付けする簡単な手続きは、探索と”伝搬”からなるものである。1が見つかるまでSを走査し、その値をまだ使われてない最初のラベルの値、たとえばvに変える。そしてvを1に向けて繰り返し(必要なら並列に)伝搬させる。すなわち、vを近傍として持つ1をvに変える。もはや変化の可能性がなくなったとき、明らかに最初のvに連結した1は全てvになっている。ここでさらに走査を続ける。別の1が見つかれば、これはv成分には属してないので、新しいラベルを付けて同じ手続を繰り返す。

Sが処理対象の画像、ラベル付けする対象が1、割り振るラベル値がvです。
この手法は「この手続は簡単ではあるが、非常に時間がかかる。各々の伝搬の過程は、たとえ並列に処理しても、図形の面積の次数だけの反復を要するからである。」と紹介されています。最初に紹介したMSのサイトで使われているのがまさにこれです。おすすめできません。

2:境界線を抽出し、輪郭内部に同一の値を割り振る

成分のラベル付けの別な方法としては、9.1.2節で述べた境界を見つける手法を、いくつかの外側境界(すなわち、Sの成分でこれを囲むS^の成分に隣接している境界。演習9参照)を別々にマークを付けるよう修正し、各成分の外側境界に異なったラベルを用いる。これが済むと、必要に応じて外側境界のラベルを成分の内側へ”伝搬”させることができる。これを並列に行うなら、たかだか図形の半径に等しい反復数を要する。

手当たりしだいに伝搬させるのではなく、まずは領域の輪郭にユニークなラベル番号を付与し、必要であれば内側に値をコピーする。1番よりは効率的ですが、「外側境界のラベル付けの手続きは時間がかかる
」処理であり、図形の面積につれて増加する多数のステップを有します。

3:行毎に横のつながり(ラン)を求め、上下の対照表をもとにつなげて行く

たいていの目的には、境界よりむしろ1のランを追跡してラベル付けをする次の手法が最良策である。図形の第1行目において、各ランに異なったラベルを与える。第2(とそれ以下の)行では、1のランを調べて前行のランと位置を比べる。ランρが前行のどのランとも隣接していなければρに新しいラベルを付ける。ρが前行のちょうど1つのランに隣接しているなら、そのランのラベルを付ける。ρが前行の2つ以上のランに隣接しているなら、ρにはこれらのらべるの(たとえば)最小値を付けるが、これらのラベルはすべて同一成分に属することも控えておく。図形がこのようにしてすべて走査されたとき、控えを分類して等しいラベルの集まりを決定できる。必要なら、図形を再走査し各ラベルを、たとえば等価な最小の値のラベルに置き換えることが出来る。

ということで、3番目がおすすめです。処理時間が画像サイズに依存して決まるし、ラスタ走査のみなのでメモリ上のキャッシュも有効に動いてくれるでしょう。

上の説明文を素直に実装すれば効率のよいラベリング処理を実現出来るでしょう。

TDD Boot Camp行ってきた

12月 20th, 2009

TDD Boot Campに行ってきました。

◯t-wadaさんのTDD話
いつものように資料があとでアップされるのではないかと思います。
個人的にとってたメモを以下に載せておきます。

会場ではt-wadaさんのテスト駆動開発本が見れるようになっていました。かなり読み込まれた形跡があって、TDD愛が伝わります。訳がいまいちというAmazonレビューで躊躇していましたが購入して読んでみようと思います。

Test Drivenの作者 Lasseさんの講演
レシーバーが足りなそうだったので英語を聞きとるのに必死になってしまい、話の所々がフォローできず。
twitterの#tddbcタグを見る方が参考になるかも。
内容はレガシーコード改善ガイドの紹介と、Coberturaのライブハッキング。Eclipseのコードさばきが見事すぎて見とれてしまいます。

◯TDD実践編
ペアを組んでサイズ制限付きのハッシュを作成しました。
いつもだとつい実装を先に書いてしまい、テストが後回しになったり、テストに抜けや不足ができてしまうのですが、ペアプロだったこともあって、TDDの基本的な流れが改めて抑えられたと思います。

ソースはgithubに上げてあります。

http://github.com/tune/lrucache

TDDを知るには完成形のソースではなくて、その過程を学ぶことが大事ですね。gitを途中から使っておきながらあまりコミットできてません。次はもうちょっと気を配らないと。

周りにすごい人もたくさんいました。

  • ペアプロでのソースの受け渡しをgithub/gistでやっちゃう
  • gistにソースあげたし、CIもやるか→ローカルHudsonでCIまで
  • 一人で時間あったしやってみたよとLasseさん、しかもJavaとRubyの2言語。Rubyのテストをみんなで見たけどテストが完結で読みやすい、ここまでできるのかと目から鱗でした。

会社ではC言語なのでCUnitを使ってテストは書いていますが、我流になっているところがあって、今日は行けてよかったです。

  • テストも製品ソースと同じく綺麗に書く → 主張はよく耳にするけど実際にLasseさんやその他の人のソースをみると上には上がいて、きちんと実践できてます。
  • 言語でテストの読みやすさ、書きやすさに差ができるわけではない。Javaでも簡潔にかけるし、Rubyでもどうしようもないコードはかけてしまう。

楽しいイベントを開催していただきありがとうございました。

インクリメンタルとイテレーティブの違い

12月 13th, 2009

[Agile]イテレーティヴとインクリメンタルの違い | Ryuzee.comを読んで思うところがあったので少し出遅れたけどメモ変わりに。

インクリメンタルだと最初から完成した姿を見越してI/Fを完全に作り込むのはあまりに難しく、かといって完成した部分に都度手を入れていては差分開発による工数の削減にならず、スケジュールが問題化してしまう。だったら前に完成したところは触らず、追加分で黒魔術を駆使しようという勢力が優勢になり、バージョンを重ねるにつれて継ぎ接ぎ部分が問題化する。機能もソース規模も雪だるま式に増えていく一方で、全体最適なソフトを作ることはきっとできない気がする。

差分開発というキーワードは一般的だけど、自分たちが取り組んでいるのは「イテレーティブ型」で、将来にわたってソフトウェアが最大の価値を生めるようにしているからだと主張しないと、ソフトウェアアーキテクチャの設計が悪いから差分開発が徹底できてないと評価を下されてしまう。

マネージャー層の階層を上がるほど「インクリメンタル」を念頭に置いている人が多いような気がします。機能が一通り揃ったら開発を収束させて、別なソフトに触手を伸ばすのもインクリメンタルな発想ありきなマネージメントかな?

【未解決】VisualStudioとgccでコンパイルできるソースのエンコーディング

11月 4th, 2009

UTF-8だと一見どっちも対応しているように見えるんだけど、VisualStudioはBOM有のみ、gccはBOM無しのみ対応しています。

んで、対策として取ったのが gitでリポジトリからのチェックアウト時に文字コードを変換する » tune webだったんですが、今日仕事中にこんなページを見つけました。
GOGA – 数式の夢とコンピュータの現実: UTF8のソースコードをgccとVCで共有すること

なんだ、VisualStudioの方は警告さえ抑えれば普通にコンパイルできるのかとさっそくやってみたのですが、見事に失敗しました。VisualStudioでコンパイルするとエラーが山ほどでてダメでした。
調べてみるとVisualStudio2003まではいけたらしいんですが、自分が使っているVisualStudio2005ではNGでした。今の最新版は2008ですし、来年には2010もでます。VisualStudioのバージョンによらず簡単な解決法を模索したいところなのですが、困りました。

「VisualStudio2008/2010ならBOM無しのUTF-8も扱えるよ」という情報をお持ちの方がいらっしゃいましたらぜひ教えてください。

gitでリポジトリからのチェックアウト時に文字コードを変換する

9月 30th, 2009

ようやく実現できたのでやり方をメモ。
設定ファイルで拡張子に基づくフィルタリングをすればOK。

ProGitの情報によるとリポジトリから取ってくるときをsmudge、リポジトリに突っ込むときをcleanと呼ぶらしい。

以下はリポジトリ内のソースファイルがUTF-8 BOM有、改行コードがLFCRの場合の設定例。文字コード変換はnkfを使っています。
WindowsのVisualStudioに合わせると上記設定が望ましいが、Linux環境でgccを使うにはBOM無しにして、改行コードをLFにする必要がある。
まず.gitconfigファイルに以下を追加する

[filter "fixencoding"]
clean = “/usr/local/bin/nkf -w8 -Lw”
smudge = “/usr/local/bin/nkf -w -Lu”

これでsmudgeでUTF-8 BOM無し/LF、cleanでUTF-8 BOM有り/LFCRとなる。

これだけではダメで、フィルタ処理をかけるファイルを指定する必要が有る。
gitの管理フォルダである.gitがあるトップディレクトリに.gitattributesファイルを以下の内容で作成し、git checkout -fする必要が有る。

*.c filter=fixencoding
*.cpp filter=fixencoding
*.cxx filter=fixencoding
*.h filter=fixencoding
*.hxx filter=fixencoding
*.txt filter=fixencoding
Makefile filter=fixencoding

/usr/share/git-core/templates/info/attributes
を作って上記内容を書いておくとclone時に.git/info以下にコピーされてgit cloneしただけで文字コード変換が動くようになる。

動かすにあたって問題となったのはgitでチェックアウトしただけで編集されたことになってしまうファイルが多々発生したことです。原因はいろいろあったのですが
リポジトリインデックス内のファイル文字コードがバラバラだった(BOM無しファイルが紛れ込んでいた とか)
ファイル内の文字に半角カナがあるとダメらしい。
ファイル内の文字に機種依存文字(実際にあったのは丸数字)があるとダメらしい。

git statusなどで編集が有ったかどうかはインデックス内の状態と比較するからcleanして元々の状態と変わってしまうと当然チェックアウトしただけで編集されたと勘違いされてしまうファイルができてしまう ということですね。
WEB DB Press Vol.50で解説されていたgitの内部データ構造を知ってようやく理解できました。

Subverisonだと文字コードをうまく変換する機構も無いのでgitをかましてやるのが便利ですね。

x86_64だと浮動小数点演算の精度が低下する

12月 9th, 2008

64bit OSで動かすと32bitの時と処理結果が異なるプログラムで悩んだメモです。

調べていくとfloatを使った浮動小数点演算で誤差が出ることがわかりました。分からなかったのが、IEEE754でfloatの演算精度は32bitと規定されているのになぜ同じソースから作ったプログラムで結果が違うのかということです。

x86では浮動小数点演算にx87を使うのですが、この演算精度は32bitや64bitではなく、80bitで行われるそうです。この辺の話は 浮動小数点演算ではまった話 – bkブログとかBK通信に詳しく載ってます。要約すると32bitの精度で誤差が生じる計算が含まれていても”たまたま”うまく計算できることがある ということです。

たまたま精度よく計算できるプログラムをx86_64に持っていってコンパイルしようとします。このとき、x86_64では必ずSSEが使えるため、コンパイラはデフォルトでSSEを使うようにコンパイルします。SSEで浮動小数点演算を行うと演算精度がx87の80bitよりも悪くなるため、本来生じるはずだった演算誤差が表面化することになります。

高林さんがBK通信で書いているMac OSで演算結果が違うというのも、Mac OSが64bitOSであることが関係している気がします。

バイナリーハックス買おうかな。

Binary Hacks ―ハッカー秘伝のテクニック100選
高林 哲 鵜飼 文敏 佐藤 祐介 浜地 慎一郎 首藤 一幸
オライリー・ジャパン
売り上げランキング: 23306
おすすめ度の平均: 5.0

5 ハードコア?なソフトウエア
5 大工さんにおける電動工具の紹介本
5 当然教科書ではない。でも、とても参考になります。
5 バイナリアンの基本

gruff

11月 30th, 2008

Rubyできれいなグラフが書けるライブラリ、仕事で数値を扱うのに使いました。使うにはImageMagickとRMagickが必要です。

リンク先のグラフはどれも洗練されていて美しいのですが、データがちょっと多かったりすると見た目が悪くなって困り者です。なんだかんだ言って細かな調整ができるExcelの方がまだ使い勝手がいいです。
細かい所に手が届かないのも問題ですが、使い方のサンプルが少なくてちょっと分かりにくいかな。

Railsでも使えるみたいなんで、もうちょっと情報が出てくるといいですね。

Gruff Graphs for Ruby | Ruby on Rails for Newbies

追記、Railsならhtml5jp_graphsなんてのもあるらしい。こっちの方が細かい所まで手が届く感じ。

Buildbotはディレクトリの移動ができない

7月 30th, 2008

会社で Buildbotを設定していた困ってしまったのですが、この手のCIツールはディレクトリの移動ができないのでしょうか?

CatalystアプリをbuildbotでContinuous Integration – dann@catalyst – Catalystグループを参考にBuildbotの設定ファイルを書いていたのですが、Subversionリポジトリからtrunkを引っ張ってきてトップディレクトリにあるmakeファイルを使ってプロジェクトをビルドする設定は

from buildbot.process import factory
from buildbot.steps import source,shell
f1 = factory.BuildFactory()
f1.addStep(source.SVN,
svnurl=source_code_svn_url,
mode=”clobber”)
f1.addStep(shell.ShellCommand, command=['make'])
f1.addStep(shell.ShellCommand, command=['make', 'test'])

と書けますが、自分が設定していたプログラムのMakefileは”project/Makefile”にあったのでcdコマンドを加えれば行けるのかと

f1.addStep(shell.ShellCommand, command=['cd', 'project'])
f1.addStep(shell.ShellCommand, command=['make'])
f1.addStep(shell.ShellCommand, command=['make', 'test'])

と書いたところBuildbotがエラーを吐いて止まってしまいました。作業ディレクトリ変更用の機能があるのかとマニュアルを一通り見てみたのですがどこにも見当たりません。

確かにConfigureスクリプトやMakefile、インストールプログラムはプロジェクトのトップに置くのが一般的なので問題になった自分のプロジェクトが変なだけかもしれません。

ビルドテスト用スクリプトを自作するか、プロジェクトの構成を変更してしまうか悩んでいるところです。
自作はさけたいんだけどなぁ。

log4XX まとめ

2月 18th, 2008

オレンジニュースlog5j – Google Codeが紹介されていたので、log4XXって各言語にあるのかなと気になったので調べてみました。

思いつく言語で見つかったのだとこんな感じかな? 他にもご存知の方がいましたら教えて下さい。