2009年03月24日

こはRubyより

ここ数日、暖かくなったり寒くなったりを繰り返していますが、
春を感じられる日々になってきましたかわいい
あとは、花粉が治まれば春を楽しめそうなのですが・・・もうやだ〜(悲しい顔)
こんにちは、ハラです。

前回の流れに沿って、RubyでXMLを読みこんでみたいと思います。
XMLの読み込みには、Rubyで実装されたXMLパーサーの
「REXML」を使ってみますひらめき

今回読み込むデータは下の通りです。
簡単なデータを定義した・・・という想定ですメモ
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="sample_data1" date="2009/03/24">
<string count="2">apple orange</string>
<number count="3">123 456 7890</number>
</data>
<data name="sample_data2" date="2009/03/24">
<string count="1">grape</string>
<number count="5">1 2 3 4 5</number>
</data>
</root>

XML読み込み、その内容を出力しているのが下のスクリプトです。左斜め下
require 'rexml/document'
# XMLファイルを読み込み
xml = REXML::Document.new(File.open('sample.xml'))
# data要素を検索
xml.elements.each('root/data') do | data_tag |
puts 'Name : ' + data_tag.attributes['name']
puts 'Date : ' + data_tag.attributes['date']
# string要素を出力
data_tag.elements.each('string') do | string_tag |
puts ' Count : ' + string_tag.attributes['count']
puts ' Text : ' + string_tag.text
end
# number要素を出力
data_tag.elements.each('number') do | number_tag |
puts ' Count : ' + number_tag.attributes['count']
puts ' Text : ' + number_tag.text
end
end

その実行結果は、下の通りになります。
Name : sample_data1
Date : 2009/03/24
Count : 2
Text : apple orange
Count : 3
Text : 123 456 7890
Name : sample_data2
Date : 2009/03/24
Count : 1
Text : grape
Count : 5
Text : 1 2 3 4 5

要素の属性や内容へのアクセス方法はそれぞれ
  次項有属性  attributesメンバに対して、属性名を使って配列アクセス
次項有内容 textメンバへアクセス

で行えます。

XML形式で出力されるデータは意外と多いですが、
C/C++で扱うとちょっと大変だったりしますたらーっ(汗)

スクリプト言語を使い、作品が直接読み込むデータへコンバートしたり、
C/C++で扱いやすい中間データへコンバートすれば、
全体の作業量はかなり減るんじゃないでしょうかexclamationわーい(嬉しい顔)

そうやって時間が作れれば、さらなる高みを目指してグッド(上向き矢印)グッド(上向き矢印)グッド(上向き矢印)です。

posted by 管理人 at 14:51 | Comment(1) | プログラミング

2009年03月12日

VisualStudioでのブレークポイント設定方法

こんばんは、平尾です。
昨日、健康診断の結果が来まして、若干数値が上がっているところがありました。
運動不足が数値として提示された以上、次回までにこのパラメータを調整しなければ!と思いました。
みなさんも悪いパラメータにならないようお気をつけください。


*

今回はVisualStudioでのブレークポイントの使い方を解説していきます。
デバッグには必須ですが、意外と知らない人もいるのではないでしょうか。


位置情報ブレークポイントの設定方法
設定によっては操作方法が違うかもしれませんがご了承ください。
ブレークポイントをセットするにはソースを開き、
セットしたい位置で F9 です。
セットするとソースの左端にこんな赤い丸が表示されます。
この状態でプログラムを起動するとその場でプログラムを停止でき、各種変数の確認や書き換えなどができます。

ブレークポイントを削除するには、同じ行で F9 です。
ブレークポイントを一時的にOFFにするには Ctrl + F9 です。
ブレークポイントを全削除するには Shift + Ctrl + F9 です。

基本操作ですね。
止まった状態では黄色い矢印が表示されていると思います。
これをドラッグ&ドロップでつまんで移動させると、実行中の位置をずらすこともできます。
ずらした場合、途中のコードは実行されません。
実行位置だけ変更することになります。


位置情報複数回実行されたときにブレークするには
ブレークポイントをセットし、そのブレークポイントに何回か来たときに止めたいことがあると思います。
そんなときはを右クリックし、ヒットカウントを選びます。
選び方には
・常に中断
・ヒットカウント数が次の数と等しいときに中断
・ヒットカウントが次の数の倍数になったときに中断
・ヒットカウントが次の数以上になったときに中断

の4種類あります。
お好みのヒットカウント(何回ブレークポイントの位置に来たか)のタイプを設定し、上記のどれかを選びます。

ここで注意事項です。
ヒットカウントはデバッガが回数を数えてくれているので、for文のループカウンタ等とは全く別に測定しています。
なので以下のプログラムで pData->update(); の位置にヒットカウントが 10の倍数のときに止めるとした場合、
for( int i=0; i<16; ++i ) {
Data* pData = getData(i);
if( pData ) {
pData->update();
}
}

1回目は i=9
2回目は i=3
3回目は i=13
4回目は i=7
5回目は i=1

といった状態で止まってしまいます。ご注意ください。
i == 10 の時に止めたい場合は次の方法↓を使います。


位置情報条件を指定してブレークするには
上記の i==10 のときに止めたい場合、ブレーク条件を指定すれば止めることができます。
を右クリックし、条件を選びます。
条件には
i == 10
を入力し、"true の場合"にチェックを入れます。
これで毎回 i == 10 のときに止まります。

"変更された場合"のチェックに入れ、条件に
i
とだけ入れると、前回来たときと比べて i が変わっていた場合にのみ止まります。
ただしブレークポイントを設定した行での値チェックになりますので、書き換わった瞬間をとらえることはできません。
書き換わった瞬間をとらえたいというときには次の方法↓を使います。


位置情報書き換わった瞬間ブレークするには
"書き換わった瞬間をとらえたい変数"に正しい値が入っている箇所でブレークポイントをセットし止めます。
次にメニューの「デバッグ」→「ブレークポイントの作成」→「新しいデータブレークポイント」を選びます。
次にアドレスに変数のアドレスを入力します。

例えばグローバルな変数 int counter があったとしましょう。
この counter が書き換わった瞬間をとらえたいときには、アドレスの欄に
&counter
と書きます。

バイト数には変数のサイズを入力します。
サイズの上限ですが、ハードウェア的に4バイトまでしか対応できないようです。

アドレスだけ分かっているという特殊な場合は、アドレスの欄に直接アドレスを書きます。
例えば
0x014a5320
といったふうに入力します。(値は適当です…)

この方法はブレーク箇所を設定しないため、書き換わった瞬間を止めることが出来ます。
なのでメモリ破壊を発見する際に大活躍します。
是非覚えておきましょう!ぴかぴか(新しい)
ただし、1つのプロジェクトにつき 4つまでしかセットできませんので注意してください。
ちなみに自動変数をこの方法で止めようとすると、全然関係無いところでも止まってしまいます。
自動変数はスタック領域からとられるためです。


*

いかがでしたでしょうか。
デバッグ作業には欠かせないブレークポイント、しっかり自分のものにして使っていきましょう!
ここでは紹介していませんがトレースポイントなんていう機能もあったりします。奥深いですね。
興味のある方は調べてみてはいかがでしょうか。
それでは皆様、素敵なプログラムライフをわーい(嬉しい顔)

posted by 管理人 at 18:42 | Comment(0) | プログラミング

2009年02月17日

コメント文の重要性

こんにちは。ザンギエフ大好き、平尾です。
すごく暖かくなったと思ったら、急に寒さが戻ってきました雪
寒暖の差が激しいので体調を崩さないか心配です。
皆様もお気をつけください。

*

今回はプログラムを学んでいる学生に向けて、コメント文の話をしたいと思います。
コメント文は実際に処理として組み込まれるわけではないですが、大変重要なものです。
プログラマとして仕事をしている方なら、よりその重要性を感じているのではないでしょうか。

コメント文が役に立つ点としては以下のようなことが考えられます。
1処理の流れや要約を簡単に知ることができる
2制作者の意図を知ることができる
3今後のためのメモを残せる
当たり前なことばかりで申し訳ありませんあせあせ(飛び散る汗)
では順番に説明していきます。



1
処理の流れや要約を簡単に知ることができる。ごく普通の理由です。
ソースコードを見れば全ての処理を知ることはできます。
しかし処理を理解するのに必要なのは、まずはおおまかなアルゴリズムだったり、処理の流れだったりします。
そんなときはコンピュータに実行させる単位で見るより、人間に分かる単位で書かれたものを見る方が圧倒的に効率がいいわけです。
自分が作ったものでも、あまりにも前のソースだと忘れたりしますが、そんなときも役に立ちます。



2
制作者の意図を残せるというのも、また大切な要素です。
例えば次のような処理があったとしましょう。
    switch( mode ) {
case MODE_INITIALIZE:
initialize();

case MODE_UPDATE:
update();
break;
}
最初のケースの中、initialize(); の後にbreakが入っていないのはバグじゃないか?と思いますよね。
でも以下のように一文入っているだけで、
    switch( mode ) {
case MODE_INITIALIZE:
initialize();
// breakせずに引き続き更新処理を行います

case MODE_UPDATE:
update();
break;
}
ああ、これは意図された処理なんだ、と理解できます。
ソースコードだけではよく分からない微妙なニュアンスも残せるわけです。



3
今後のためにメモを残せるという項目についてです。
自分でルール付けをして書いておけば、後から行う最適化やリファクタリングのときに便利です。
例えば、今後するべきことを書いておく場合。
    // TODO:とりあえずバブルソートで実装。今は問題無いが、
// 処理が重くなってきたときに別のソートに置き換える必要あり。
といった風に書いておけば、TODO:でgrepして見つけることが出来ます。
こうやって書いておく癖をつけておけば、後でやることを忘れていた、なんてことが減ると思います。



exclamation&question
ただ、何でもかんでもコメントさえ書けばいいというわけではありません。
例えば次のようなものはNGです。
    // 登録個数ループ。
for( int i=0; i<count; ++i ) {
:
:
}
コードを見たまま書いているようなコメントではあまり意味がありません。
今まで説明してきたように、処理の流れや意図が分かるように書く必要があります。
次のような感じです。
    // 登録されているものから検索。一番始めに見つけたデータを使用する。
for( int i=0; i<count; ++i ) {
:
:
}
これならば中で何をしたいのかがよく分かると思います。

*

コメント文という、プログラムにおける初歩の話をしましたが、少しはお役に立てたでしょうか?
役に立つようでしたら、これからもこういった基礎的な話などを書いていきたいと思います。


P.S.
会社説明会のエントリー状況がとても好調で、3/25の東京会場は残りわずかとなっています。
弊社に興味のある方はお早めのエントリーをお願い致します!ぴかぴか(新しい)

posted by 管理人 at 16:05 | Comment(2) | プログラミング

2008年12月17日

コーディングルール

こんにちは!イワサキですわーい(嬉しい顔)
今日はプログラムのコーディングルールについてお話してみたいと思います。

コーディングルール』とは何か?

コーディングルールは、プログラムのソースコードの記述をするときの文法作法のルールのことです。
例えばC言語でもっとも一般的なコーディングルールの例は次のようなものです。

【作法A】 中括弧を構文の右側に付ける


if( a > 0 ) {
   a = 0;
} else {
   a = -1;
}


【作法B】 中括弧を改行して構文の下側に付ける

if( a > 0 )
{
   a = 0;
}
else
{
   a = -1;
}


C言語にしばらく触れたことがある人は体験したことがあると思います。
これは言語そのものが改行箇所を定めておらず自由度の高いコーディングスタイルで記述できるようにという配慮からこのような記法があるのですが、極端な話ではこれを次の例のように一行に記述することもできてしまいます。ペン


【作法C】 一行に収める

if( a > 0 ) { a = 0; } else { a = -1; }



上記の記法AとBとCどれでもプログラムは問題なく動作します。
それぞれに長所と短所があります。

作法Aの場合
総行数が少なくて済む
× 括弧の位置関係が見にくい(開く・閉じるが視覚的に同列に無い)

作法Bの場合
フロー内部の処理と開始終了の区切りが見やすい
× 行に余白が多くなり間延びしてしまう(括弧1文字に一行を消費したりする為)


10年以上前のPCではハードウェアの制約で一画面に表示できる文字の行数が少なかった(20行〜30行だった)ため作法Aのスタイルがよく使われていました。
今でもそのメリットは有益ですのでこの手法をとるプログラマは多いと思います。

このようなコーディングスタイルを一貫して統一して記述しましょうというのがコーディングルールです。
さまざまな作法がごちゃごちゃに混ざったソースコードはやはり読みにくいですよね。
ここで大切なのはそのコーディングスタイルが優れているかというよりも第三者がソースコードを見るときに見やすいかどうかのほうが重要だということです。
特に企業レベルで制作するプログラムは一人ではなく複数人数で分担して一つのものを作成していきます。
そのときにお互いのソースコードが同じ書き方でなければお互いのプログラムソースコードを読むときに読みにくく感じてしまいます。
チームや会社のような大きなグループ単位でこのようなスタイルの統一を図っておけば少しでも快適な制作になるだろうと思います。

今回の例は中括弧のみでしたが、コーディングルールではほかにも様々なルールを決めることもあります。
クラス名、変数名についても大文字小文字の区別やアンダーバー、プリフィクス(ポインタにはpDataなど名前の先頭に"p"をつけるなど)やコメント文の書き方、多岐にわたって決めることもあります。
これもプログラマ同士で円滑にソースコードを運用していく為の知恵なのですひらめき

コーディングルールは作法のどれが正しくてどれが間違っているかということは一切ありませんので個々の主観によると思います。
チーム内でA派とB派に分かれて対立!むかっ(怒り)なんてことにもなりかねません(ならない!?)
ヘキサドライブでは現在、しっかりとコーディングルールを取り決めて制作の時に運用していますぴかぴか(新しい)ぴかぴか(新しい)ぴかぴか(新しい)
ソースコードは会社とプログラマの資産ですからね!パンチ
統一されることで、より一体感のあるプログラムソースコードとなるでしょう。
作品だけでなく、そのときに作られるもの全てを大切にしていこうという気持ちがヘキサドライブにはあります。

私事ですが先日、子供が産まれて命名に迷って迷って大変でした晴れさりげなくカミングアウト!?
名前を付けるって大変ですね。変数名もクラス名も、人間も! 人間も!?
みなさんも良いコーディング生活をおくってくださいね手(パー)ぴかぴか(新しい)

posted by 管理人 at 18:15 | Comment(0) | プログラミング

2008年11月19日

ソコにSTLがあるから。

最近、会社への行き来の時間にゲームで遊ぶことが
日課になりつつある今日この頃電車
電車の乗り過ごしには気をつけないといけないですね・・・がく〜(落胆した顔)
こんにちは、ハラです。

前回に引き続き、今回もSTLについて触れてみたいと思います。
今回は、連想コンテナであるmapについての小ネタです。

1operator[]とinsertメソッドを使い分ける
mapに対して新たに要素を追加する場合、
operator[]とinsertメソッドの2つの方法があります。
実際に書くとこんな感じです。左斜め下

DataObjectMap dataMap;

// キー「1」値「100」として追加
dataMap[1] = 100;

// キー「2」値「200」として追加
dataMap.insert(DataObjectMap::value_type(2, 200));

一見するとoperator[]の方を使いたくなりますね。
なんと言っても見やすいひらめき

ですが、この2つはちょっとした違いがあります。
operator[]の場合、insertメソッドと比べて、一時的なオブジェクトの
生成と破棄が余分に行われますたらーっ(汗)

今回のコードでは、オブジェクトといっても通常の整数型なので
生成と破棄のコストを気にすることはありませんが、
複雑で巨大なオブジェクトの場合は、気にする必要が出てくるかもしれません。
そんな時は、ちょっと長いinsertメソッドを使ってみてください。

2ポインタを入れる場合は、解放タイミングに注意を
解放を忘れない・・・・当たり前のことではありますが、
追加と削除が行われる頻度の高いmapではちょっと大変です。

mapは、自分が管理しているオブジェクトが
何処から参照されているかを知りませんバッド(下向き矢印)

そのため、ポインタを解放してよいかどうかを自分で管理することになります。
対策としては、「参照カウンタを導入する」などでしょうか。

もしくは全体を見直して、最初に要素を追加、
最後に全ての要素を解放と出来るようにするのも良いかと思います。

事前に準備をしておき、後で困ることなくmapの恩恵を受けたいものですね。


mapはとても便利な上、同じ機能を自分で作るとなると手間がかかります。
連想コンテナが必要になった時には、選択肢の1つとして考えてみてはどうでしょうかグッド(上向き矢印)

posted by 管理人 at 11:00 | Comment(0) | プログラミング

2008年10月27日

突撃!STL!!

ここ最近ぐっと冷え込み、虫の鳴く声もよく聞くようになりました。るんるん
10月も終わりに近づき、秋らしさを日々実感しちゃいますね。アート
こんにちは、ハラです。

今日は、STL(Standard Template Library)について書いてみたいと思います。
STLとは、C++の標準ライブラリにも含まれているコンテナと
アルゴリズムに関するライブラリのことです。
コンテナは、動的な配列やリスト、連想配列などのデータの入れ物ビール
アルゴリズムは、ソートやマージなどなど、上のコンテナに使うものですひらめき

上手く使えば、短い時間で良いプログラムが書けてしまう便利なものですぴかぴか(新しい)
そこで、STLの機能を知っていて使おうかなと思っている方、もしくは使い始めたばかりの方向けに、コンテナの1つ、vectorについての小ネタを書いてみます耳

それでは、小ネタに移ってみましょう〜グッド(上向き矢印)

1ループの際に一時変数に入れるとお得
vectorは配列の代わりに使うことが多いため、下のようなループを書くことがあります。

void TestFunc(std::vector< Data >& dataVec)
{
for( size_t i = 0; i < dataVec.size(); i++ ) {
dataVec[i].get();

// 何かの処理

dataVec[i].set();
}
}


ループの中身は、sizeメソッドの呼び出しを除いて普通の配列と同じかと思います。
でも、この見慣れたコードに対してデバッグを行うと見慣れないことが起きるかもしれませんexclamation&question

環境によっては、デバッガで「dataVec[i]」の値を直接見ることが出来ない場合があるのですあせあせ(飛び散る汗)
ちなみに、私の手元の環境では見れませんでしたもうやだ〜(悲しい顔)
この状況を解決するため、以下の変更を加えてみましたひらめき

void TestFunc(std::vector< Data >& dataVec)
{
for( size_t i = 0; i < dataVec.size(); i++ ) {
Data& data = dataVec[i]; // ここで一時変数に入れてます
data.get();

// 何かの処理

data.set();
}
}


これは、以前の日記で書かれていたことと同じ変更ですが、
今回は、デバッガで値が見れるようにもなりますひらめき

このコードをデバッガで動かすと、「dataVec[i]」の値は見れなくとも「data」の値は見ることが出来るため、中身の確認が出来るようになるというわけです。
1行追加することで意外な効果がありますね。

2reserveメソッドでサイズの自動拡張を防ぐ
vectorには、push_backメソッドという、末端に要素を追加する便利な機能があります。
便利ではありますが、vectorには格納できる要素数を超えた場合、
自動でサイズを拡張する性質があるのでちょっと注意が必要ですあせあせ(飛び散る汗)
例えば・・・・

std::vector< int >    valueVec;
for( int = 0; i < 1000; i++ ) {
valueVec.push_back(i);
}


と書いた場合、ループを抜けるまでに複数回のサイズの拡張が行われることになります。
サイズの拡張とは、
    ・新しいメモリ領域の確保exclamation×2
・古いメモリから新しいメモリへのコピーexclamation&question
・古いメモリ領域の開放exclamation&question

が1セットとなって行われます。
う〜〜ん、出来れば避けたいものですもうやだ〜(悲しい顔)

そこで、reserveメソッドを使って事前に格納できる要素数を増やしておきます。

std::vector< int >    valueVec;
valueVec.reserve(1000);
for( int = 0; i < 1000; i++ ) {
valueVec.push_back(i);
}


この変更を加えると、1000個までの要素を格納できるので、ループの中でサイズの拡張が行われることはなくなりますわーい(嬉しい顔)

ちなみに、許容量を増やすだけなのでreserveメソッドを呼んだ直後の要素数は0ということにご注意ください。

3使わないという選択肢も忘れずに
ここまで書いておいてなぜexclamation&questionという気もしますが、使わないことも選択肢の1つです。
vectorは、通常の配列と同じ使い方が出来る上に、サイズの取得や
要素の追加・削除、動的な拡張などが行える便利な物です。
便利なだけに「まずは使ってみよう」と考えてしまいますが、
立ち止まってみて、本当に必要か考えてみるのも良いかもしれません。

初めから必要なサイズが固定とわかっているなら、普通の配列という選択肢がありますし、
要素の追加削除が頻繁に行われるならlistという選択肢もあります。
必要な機能と状況に合わせて、使うかどうかを決めてみてくださいわーい(嬉しい顔)


STLのvectorを使おうと思っている方、使い始めた方、ちょっとは参考になったでしょうか?
機会があれば、他の機能についても触れてみたいと思います。

posted by 管理人 at 11:32 | Comment(0) | プログラミング

2008年09月30日

階層

全国の皆様おはようございます。平尾です。
大阪ではここ1週間で、ぐっと気温が下がりました。
先週水曜日の時点で気温が28/17度(最高気温/最低気温)あったのが、
昨日月曜日では最高気温17/14度という…。
http://weather.yahoo.co.jp/weather/jp/past/27/6200.html
さすがに社員の何名かは少し体調を崩したようで。
皆様も十分にお気を付けください。



本日もプログラマ初心者の方に向けての話です。
学生さんのソースを見ていると、ネストが深くなってしまっているのをよく見かけます。

void proc(void)
{
if( ... && ... ) {
if( ... ) {
if( ... ) {
for(;;) {
}
}
}
}
}

処理的にきっちり動いていても、
担当が交代になったときに見直すときや、
後から仕様が変わったときの修正に苦労するのではないかと思います。
こういった判定が続くような時は、別関数にしてまとめています。

void proc(void)
{
if( check() ) {
for(;;) {
}
}
}

bool check(void)
{
if( !... ) {
return false;
}

if( !... ) {
return false;
}

if( !... ) {
return false;
}

if( !... ) {
return false;
}

return true;
}

判定の仕方は人それぞれあると思いますが、
個人的には一番下まで処理が来たら true で返るような処理の流れにしています。
ひとつひとつチェックして false で返しておくと、
それぞれにDebugOutputString()などのメッセージをはさみやすいのと、
トレースしたときに失敗している箇所が分かりやすいというメリットがあります。
また書き方を統一しておくと詳細まで見なくても流れが分かりますので、
保守をしやすくなります。

チェック関数としてまとめておけば関数名で何の判定をしているのか分かりやすくなるのと、
メインの処理が短くなるため処理の流れを追いやすくなります。
処理の流れが追いやすい=バグ発見がしやすいことにもつながってきます。


ちょっとした工夫ひとつでスッキリしたプログラムになります。
別関数に分けるのは多少面倒ですが、できるところはキッチリ組んでいきましょう!
輝ける明日のために!(笑)

posted by 管理人 at 10:59 | Comment(0) | プログラミング

2008年08月27日

プログラムを書くにあたって注意していること

こんにちは平尾です。
ビルの前にいる子猫達との距離が、少しずつ縮まってきました。
この調子でいけば触れさせてくれる日も近いかもしれません



本日はプログラムを書くときに注意している点について話したいと思います。
イワサキとは打って変わって、プログラマ初心者向けの話です。


私が一番気をつけている点は繰り返しをしないことです。
for文とかそういう意味合いではなく、
「同じ処理をコピー&ペーストで書いて放置しない」
ということです。

2つ以上同じ処理があると、
バグが発生した際のメンテナンスで見落とす可能性がある
ので、
こういったものは関数としてまとめ、関数を呼び出す形にしています。
また2箇所から呼び出しているのなら、3箇所に増える可能性も大いにあるわけです。
(そんなこともあろうかと…真田さんスタイル
こうすることでコードの分量が減り、見やすくなるうえにメンテナンスもしやすくなります。一石二鳥でするんるん



変数などでも同じことが言えます。
例えば

Data data[10];
for(int i=0; i<10; i++) {
int value = data[i].getValue();

:
:

data[i].setValue(value);
}

という処理があったとしましょう。
この処理の中で data[i] が複数回使われているなら、それらもまとめておく方がすっきりします。

Data data[10];
for(int i=0; i<10; i++) {
Data* pData = &data[i];
int value = pData->getValue();

:
:

pData->setValue(value);
}

逆に一行増えてマズそうに見えますが、
もし i が何か別のインデックスから参照する形に変わったとしたら…

Data data[10];
Index index[10];
for(int i=0; i<10; i++) {
Data* pData = &data[index[i]];
int value = pData->getValue();

:
:

pData->setValue(value);
}

最初の pData に代入する箇所だけ変更すれば済みます。
エレガントです。


こうやって
「まとめられるものは、できるだけまとめておく」
という癖をつけておくと、後々楽になると思いますわーい(嬉しい顔)
後からいろいろ変更するのは面倒ですから(笑)

posted by 管理人 at 13:15 | プログラミング

2008年08月22日

分岐しないコード part2

こんにちはイワサキですわーい(嬉しい顔)
今回もコーディングテクニックを紹介してみたいと思います。
以前分岐しないコードについて書いてみましたが、今回も別のシチュエーションでの
お話をしてみたいと思います。

たとえば次のようなif文のCプログラムがあったとします。

signed int a, b;
signed int c;

if( a < b ) {
  c = a;
} else {
  c = b;
}


これをx86系の通常のアセンブルコードで書くと次のようになります。

    mov    eax, [a]
    mov    ebx, [b]
    cmp    eax, ebx
    jge    l1      // 分岐
l0:
    mov    ecx, eax
    jmp    l2      // 分岐
l1:
    mov    ecx, ebx
l2:
    mov    [c], ecx


ここから分岐を一回無くすこともできますが、現状では2回とします。
動きを図で表すとこのような具合ですね。

flow_ifthenelse.gif

そして下記のコードが分岐を無くした最適化コードです新幹線

    mov    eax, [a]
    mov    ebx, [b]
    cmp    eax, ebx

    sbb    ecx, ecx
    // ↑ここで0xffffffff または 0x00000000のマスクが生成できる

    and    eax, ecx   // eax = a & mask
    not    ecx
    and    ebx, ecx   // ebx = b & ~mask
    or    ebx, eax   // ebx = (a & mask) | (b & ~mask)

    mov    [c], ebx


マスクを生成してそれによって選択する方法です。
最近ではこういった選択実行をするものや
数値入れ替えを行なう命令がCPUに搭載されていることが多いですから、
さらに良い実行コードを出力できることになります。
多少コードが長くなっても分岐しないコードは安定して
コードが実行されるため高速実行できることが多いです。
もとの分岐するコードと比べて何倍・何十倍も実行速度が
速くなる場合があります。CPUが命令を解読する事前デコード
事前実行が行われやすくなります。
稀に処理内容によっては命令数の増加よりも分岐予測や
並列実行が影響して分岐したほうが速いこともあります。
それぞれケース・バイ・ケースですね。

最新のCPUトレンドを追う一方でプログラマもそれに合わせた
プログラミングが求められています。
C言語など高級言語が一般的になった最近でも自らの最終兵器として
アセンブラ技法を習得しておくことは無駄ではありません。
プログラミング技術を磨くために挑戦してみませんか?

ゲームプログラミングの世界では最後の切り札とされる
アセンブラプログラミング!
やってみるとパズルのようでまた違った世界があります。

大量に表示したいときや大量に動かしたい場合にこういった
高速化技法が活きてきます。プログラマの努力によって可能性は
広がります。このような細かい技法をたくさん持っておくことで
いざというときに役立ちますひらめき

今後はリアルタイムレンダリングやAIなど時折紹介できたらと
思います。
ヘキサドライブは楽しく、そして力強いゲーム開発をこれからも
続けていきますパンチ

posted by 管理人 at 17:12 | プログラミング

2008年04月15日

分岐しないコード

こんにちは、イワサキです。
前回に引き続きプログラミングの話題をお送りします。

昨今のCPUは10年前とは大きく変貌しています。
昔通用したアセンブラのテクニックや高速化のテクニックは
通用しなくなっていることが多いです。

たとえばPentium4に限って言えば、次のような現象が起こります。
値を8倍するコードです。

【左へ8bitシフト】
shl eax, 3

【2の3乗=8 を加算で表現】
add eax, eax
add eax, eax
add eax, eax


ぱっと見たら前者のほうが速そうに思いますが実は後者のほうが幾分高速です。新幹線
初めて見ると疑いたくなるような光景ですが・・・
これはPentium4の深いパイプラインが影響しているといえます。

このようにそれぞれのCPUに得手・不得手があり、
プログラマはそれに適応していくことが求められます。
ゲームの世界でも同じく高速なプログラムを書くことは大切です。
1秒間に60枚ものCG画像を生成するわけですので、
限られた時間の中でCPUはベスト手(グー)を尽くさなければなりません。
プログラマには、より高速なコーディングが求められます。

そして今日のお題。「分岐しないコード」。
最近のCPUは流れ作業的に実行されているパイプライン仕様です。
つまり作業中に別のところへジャンプすると、
そこでCPUには作業の仕切り直しバッド(下向き矢印)が発生します。

たとえば32bitの中の1が立っている数を数えるとき、
普通に考えたらこう書きますよね。

int value;
int bitCount = 0;
for( int i=0; i<32; i++ ) {
  if( (value >> i) & 1 ) bitCount++;
}


これを分岐無し(branch free)で書くとこうなります。左斜め下

int value;
int bitCount;
value -= ( (value >> 1) & 0x55555555);
value = (((value >> 2) & 0x33333333) + (value & 0x33333333));
value = (((value >> 4) + value) & 0x0f0f0f0f);
value += (value >> 8);
value += (value >> 16);
bitCount = value & 0x3f;


forやifが一切ないプログラムですが、同じ結果が得られます。
アツいですね〜ぴかぴか(新しい)
しかも前者のループを用いた方法よりもかなり高速です。
他にもソートや数値の飽和演算などにもこういったワザが有効です。
プログラミングにはパズルを解くような面白さも兼ね備えています。
もしその面白さがゲームを作りながら体験できるとしたら幸せですネ。ぴかぴか(新しい)ぴかぴか(新しい)

人生の岐路に立ったとき、分岐せず思った道をまっすぐ突き進む!
そんな熱いあなたの心はきっとプログラマー色です

熱いプログラマ来たれ!パンチ
ヘキサドライブはそんなあなたを応援します。

posted by 管理人 at 23:05 | プログラミング