お世話になります。
VC++、CUIアプリです。
実行速度的に以下のどちらが処理的に早いでしょうか?
ちなみに自分は…
あ)確保場所がひとつで済む。
い)解放もひとつで済む。
という理由で(1)を積極的に採用しています。
# この場合、メモリへのアクセスがアライメントやワード境界とか
# から外れるので、逆に遅くなるのでしょうかね?
一般的にはどちらを採用しているのでしょう。。
#define Asiz (254)
#define Bsiz (123)
#define Csiz (Asiz * 245 + Bsiz)
(1)必要なメモリを一気に纏めて動的確保。
void func(void)
{
const unsigned int siz = Asiz + Bsiz + Csiz;
char* buf = new char[siz];
char* buf_a = buf;
char* buf_b = buf_a + Asiz;
char* buf_c = buf_b + Bsiz;
:
何かしらの処理
:
delete [] buf;
}
(2)必要なメモリを個別に動的確保。
void func(void)
{
char* buf_a = new char[Asiz];
char* buf_b = new char[Bsiz];
char* buf_c = new char[Csiz];
:
何かしらの処理
:
delete [] buf_a;
delete [] buf_b;
delete [] buf_c;
}
func の開始時点で必要になり、終了時点で不要になる変数ならば
new/delete などせず普通に自動変数にする。
人によって考え方はいろいろだと思いますが、
自分なら読みやすいコード(つまり(2)の方)を選択しますね。
処理速度を比較したければ、上記の2つの処理を
1000回くらいループした時間を測定してみればすぐに分かるのでは?
GetTickCount()のようなAPIを使えば簡単だと思います。
実際に計測してみました。
1000回ではほとんど差がでなかったので10000回ループさせました。
(1)のケース:203ms
(2)のケース:437ms
10000回のループで約200msの差がありました。
この差をどう受け止めるかはプログラマー次第ですね。
ご参考までに・・・
確かに、どちらを選ぶかはプログラマ次第だと思います。
ただ、一つだけ思うのは、(2)こそ誰もが思い浮かべるソースコードであり、
(1)はテクニック系です。
私自身も同様にテクニックに頼った直感的でないソースコードを書くことがあり
ますが、その際、気をつけているのは「必ずコメントを書く」ということです。
今回の場合なら、以下のような感じかな。
// new/deleteの高速化のため、3つのバッファをまとめて確保しています
char* buf = new char[siz];
こういった、ほんの少しの気遣いがあるかないかで、あとからソースコードを追
う人に対してどれだけ助けになるか、ということです。
>一般的にはどちらを採用しているのでしょう。。
提示の内容だけでは「何とも言えない」が、自分の意見です。
buf_a、buf_b、buf_c、がそれぞれどんな「意味」を持つもので、
それらの関係性によると考えます。
そもそもbuf_a、buf_bが、意味的にbuf_cの部分であるなら(1)で
コードするでしょう。
全てが無関係のものであれば(2)でコードするかもしれません。
自分としては
「何かしらの処理のコードを無視してデータの確保方法を決めることはできない」
と考えます。
> 実行速度的に以下のどちらが処理的に早いでしょうか?
考えるまでもなく、メモリ確保/開放の回数が少ないので(1)が早いでしょう。
あくまでメモリ確保/開放の時間だけで、メモリアクセスの時間そのものは
変わりないでしょう。
> # この場合、メモリへのアクセスがアライメントやワード境界とか
> # から外れるので、逆に遅くなるのでしょうかね?
この例では char なのでアクセス時間そのものは変わりないでしょうね。
ただし、char 配列をワードアクセスした場合は遅くなったり、環境によっては
実行時エラーが発生することがあります。アライメントを守らないとアクセス違
反になる環境だってあるんですよ。
> 一般的にはどちらを採用しているのでしょう。。
一般的なのかどうかはわかりませんが、私の場合、この程度の差ならソースコードの
保守性を重視します。つまり読みやすいコードで書くべき。
その観点から考えると(2)を採用するし、周囲にも(2)を薦めるでしょう。
まぁこの程度なら、素直に
> new/delete などせず普通に自動変数にする。
ですね。
みなさん、ありがとうございます。
>tetrapodさん
> new/delete などせず普通に自動変数にする。
例で示した確保サイズ位なら、自分も同様の対応をしていると思います。
ありがとうございます。
>MistyGreenさん
>ご参考までに・・・
わざわざ時間計測までして頂いて恐縮です。
ありがとうございます。
>bunさん
>こういった、ほんの少しの気遣いがあるかないかで、あとからソースコードを追
>う人に対してどれだけ助けになるか、ということです。
>仲澤@失業者さん
>そもそもbuf_a、buf_bが、意味的にbuf_cの部分であるなら(1)で
>コードするでしょう。
(1)でも(2)でも別々に領域を割り当てているかと思います。
それぞれが関係あるか否かは、別問題かと思われますが。。
>maruさん
>> # この場合、メモリへのアクセスがアライメントやワード境界とか
>> # から外れるので、逆に遅くなるのでしょうかね?
>この例では char なのでアクセス時間そのものは変わりないでしょうね。
>ただし、char 配列をワードアクセスした場合は遅くなったり、環境によっては
>実行時エラーが発生することがあります。
実は此処を伺いたかった所です。
最適化の指示内容(コンパイラオプション)によっては、アセンブラに落ちた時にSSE(2/3
/4…やMMXもあるのかな?)等に置き換えられますよね?
当然、その様にコンパイルされた物は処理時間は短くなります。
であるならば、例えば動的にメモリ確保を行う場合でも影響が出て来るのかな?と思った
次第です。
>アライメントを守らないとアクセス違反になる環境だってあるんですよ。
存じております。
失礼しました。
>bunさん
>こういった、ほんの少しの気遣いがあるかないかで、あとからソースコードを追
>う人に対してどれだけ助けになるか、ということです。
実コード上では、厭と言うほどにコメントは入れてますので問題は無いかと思います。
ありがとうございます。
> 最適化の指示内容(コンパイラオプション)によっては、アセンブラに落ちた時にSSE
(2/3
> /4…やMMXもあるのかな?)等に置き換えられますよね?
> 当然、その様にコンパイルされた物は処理時間は短くなります。
>であるならば、例えば動的にメモリ確保を行う場合でも影響が出て来るのかな?と思っ
た
> 次第です。
そんなことを気にしていたのですか。
メモリの配置により性能が変わってくるとすればキャッシュの影響のほうが大きいで
しょう。MSDNにキャッシュの影響を考えてどのようなコードを書くべきかというペー
ジがありましたが、残念ながらどうやってたどり着いたか忘れてしまいました。
個人的にいえば、性能をそこまで考えてコーディングするのはどうかと思います。
プログラムの速度性能が高い(高速である)ことは良いことですが、それより品質
が高いことが重要であることは自明ですよね。だから小手先で高速に動作するコー
ドより可読性の高いコードのほうが良いと考えます。
実際に性能が問題になったときにこの手の高速化は雀の涙/焼け石に水ということ
になると思われ、もっと他の手段が求められるはずです。
それに新しい技術が出ることによってプロセッサアーキテキチャーなんてすぐに変
わってしまいます(根本はあまり変わらないかもしれないが)。キャッシュのサイ
ズが変われば最適なコードも異なるかも知れません。
だから、知識として知っておくのは良いことですが、この程度のことを気にするの
はどうかと。。。
長時間稼働システムでメモリの確保と解放を繰り返した際、徐々にメモリの取得に時間
がかかって性能がおちてゆくケースが昔(Win95のあたりで)は、あった記憶がありま
す。(今はそうゆうのを組んでいないで知りませんが)
動的なメモリが必要なシステムで、頻繁にメモリの確保・解放を行わないように、最初
に大きなメモリを用意して自前でメモリを提供する事は、信頼性のために必要な場合も
あるとおもいます。
でも(1)のコードは周りの目が怖いので、メモリ管理用のtempleteクラスといった形で
それらしく作ります。
(領域が足りなくなったら拡張するとか、確保・解放の整合性のチェックとか、解放時
のメモリ破壊チェックなど、いろいろてんこ盛りで)
この手のネタは好きです。
提示された条件がかなり曖昧なのでどちらとも言いかねますね。
実際の話、このレベルの処理時間の差を気にせざる得ない環境と
気にするよりもソースの可読性を重要視する環境があると思います。
組み込み系で動作環境がプアな場合、気にせざるえないケースはあります。
この場合は、多少リスキーでも処理速度を優先するケースはあります。
常にでは有りませんが、可読性を犠牲にする事も珍しくは有りません。
組み込み系でない場合は、僅かな処理時間と引き換えにソースの可読性を
犠牲にするケースと言うのは殆ど無いと思います。
皆無とは言いませんが、実際の処理時間には殆ど影響しないケースが
殆どだと思います。
仲澤@失業者さんが書かれているのも確保の仕方に可読性の要素を
入れたいという話ではないかと思います。
別々に確保した方が独立した要素であるとわかりやすいという話だと
思いますよ。元々一塊のデータで、それぞれはその構成要素と言う
話ならまとめて一括確保をいうのもわかるという意味だと思います。
PATIOさん、わざわざ説明していただいてありがとうございます。
悠さんへ
>(1)でも(2)でも別々に領域を割り当てているかと思います。
>それぞれが関係あるか否かは、別問題かと思われますが。。
自分の認識では、buf_a[254] のアドレスがbuf_b[0]のアドレスと
同じであることが、コード上で保障されているのに、
buf_aとbuf_bが無関係であるとは考えません。
たぶん、多くのプログラマがそう感じると思います。
(1)の場合、「処理」の部分で何らかの関数foo(char *)にbuf_aを渡した場合、
buf_b、buf_cの内容にも影響がでる可能性があり、
もちろんそれを期待したコードを書くことが普通でしょう。
一方(2)の場合は、「処理」の部分でそういうことが「できてほしくない」
という意思が、コードから明瞭に伝わります。
本来「処理」のすべきことが「データの構造」を決めるのであって、
「データの構造」が「処理」のコードを束縛すべきではないと
考えます。・・まぁ、ありがちなんですけどね(vv;)。
誰か話すだろうと思っていた話が出てこないので一つ。
以下のような考え方もある。
struct TBuf
{
char buf_a[254];
char buf_b[123];
char buf_c[254 * 245 + 123];
};
TBuf* lpBuf = new TBuf;