質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

4回答

3864閲覧

外部ファイルのグローバル変数にアクセスするときはポインタでアクセスしたほうが速いですか?

takey

総合スコア312

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2016/12/16 01:45

###前提・実現したいこと
組み込みのファームウェアを開発しています。変数へのアクセス速度などが気になりましたので、質問します。

外部ファイルのglobal.hには、組み込み機器が保持する変数がまとめられて宣言されています。
メインループの中で、global.hに宣言されている変数にアクセスするのですが、普通にアクセスするプログラムと、ポインタを使ってアクセスするプログラムは、速度の観点からして、ポインタを使うほうが速いと思っているのですが、合っていますか?できれば理由もおしえてください。

###ソースコード
####global.h

C

1typedef struct __attribute__((packed)){ 2 DWORD total_tick; //トータル経過tick 3 DWORD pre_tick; //前回tick値 4 WORD total_sec; //トータル経過秒 5}Param4Judge_Output; 6typedef struct __attribute__((packed)){ 7 BOOL radio_checked; 8 BYTE contact_type; 9 Param4Judge_Output jdg_output; //判定用tickパラメータ 10}ControlType_Timer1; 11typedef struct __attribute__((packed)){ 12 ControlType_Timer1 timer1; 13}ControlType; 14ControlType controltype;

####main.c

C

1#include "global.h" 2...(省略) 3 4int main(){ 5 ...(省略) 6 /* ループ内で、各タスクを呼び出す形で機能を実装しています */ 7 while(1){ 8 taskA(); 9 taskB(); 10 ... 11 } 12} 13 14void taskA(){ 15 update_controltype_tick_ver1(); // 直接アクセスするバージョン 16 update_controltype_tick_ver2(); // ポインタでアクセスするバージョン 17 // どちらが速い? 18} 19 20void update_controltype_tick_ver1(){ 21 DWORD now_tick; 22 now_tick = GetTick(); 23 controltype.timer1.jdg_output.total_tick += now_tick - controltype.timer1.jdg_output.pre_tick; 24 controltype.timer1.jdg_output.pre_tick = controltype.timer1.jdg_output.total_tick; 25 ...(処理が続く) 26} 27 28void update_controltype_tick_ver2(){ 29 DWORD now_tick; 30 ControlType *p; 31 p = &controltype.timer1.jdg_output; 32 now_tick = GetTick(); 33 p->total_tick += now_tick - p->pre_tick; 34 p->pre_tick = p->total_tick; 35 ...(処理が続く) 36} 37

###補足
関数のプロトタイプ宣言などは省略しています。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

速度の観点からして、ポインタを使うほうが速いと思っている

なぜそう思われたのでしょうか。たしかに、JavaScriptやRubyといったスクリプト言語では、オブジェクトのメンバアクセスはその都度プロパティ参照を解決する必要があるので遅くなることがあるかもしれません。

ただ、C言語では構造体の構造もすべて静的に決まりますので、メンバ参照もコンパイル時に解決できます(継承できない以上、C++のようなvtableが入ってくる余地もありません)。普通にメンバ参照する方では静的変数の位置を決め打ちできるのに対して、(最適化されて消える可能性はありますが)ポインタ経由の参照ではポインタをアクセスするコストがかかるので、むしろ遅くなるのではないかと思います。

どちらにしても、そのレベルで速度を考えないといけないのであれば、できたバイナリを逆アセンブルして判断する、ぐらいのことは必要だと思います。

投稿2016/12/16 02:17

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takey

2016/12/16 04:43

構造体の変数a.b.c.memberがコードに現れたとき、最終的にmemberにアクセスする時間Tは、 (aにアクセスする時間) = t_a (bにアクセスする時間) = t_b (cにアクセスする時間) = t_c (memberにアクセスする時間) = t_member とすると、 T = t_a + t_b + t_c + t_member だと思っていました。 つまり、コード上にa.b.c.memberが現れるごとに、(t_a + t_b + t_c + t_member)時間かかるという考えです。 それを、ポインタをp = &a.b.cのように使うことで、最初のpに代入するときだけ(t_a + t_b + t_c)時間かかり、それ以降はt_member時間だけで済むと考えていました。 ところがそうではなく、コンパイル時点でa.b.c.memberのアクセス先は決め打ちされる(アクセス時間はtに決め打ちされる)ので、コード上にいくらa.b.c.memberがでてきても、一律t時間でアクセスできるということでしょうか?
maisumakun

2016/12/16 04:54

C言語の場合、構造体も型が決まっているので、「a.b.c.member」へのアクセスは、「(aのアドレス+bのオフセット+cのオフセット+memberのオフセット)へのメモリアクセス」という一度で済みます。実際に「a.b」や「a.b.c」へアクセスする必要がまったくないわけです。
takey

2016/12/16 07:06

あまりわかっていないのですが、構造体のメンバ変数へのアクセスは一度で済むと読みました。 つまり、「aのアドレスを読みに行って、bのアドレスを読みに行って、cのアドレスを読みに行って、memberのアドレスを読んでアクセスする」という順序ではなくて、いきなり「memberのアドレスを読んでアクセスする」という動作をしているという理解です。 回答ありがとうございました。
guest

0

ベストアンサー

遅くなるも速くなるもターゲットのアーキテクチャやコンパイラの最適化に依存します。

ところで、計測しないということはパフォーマンス(速度)には関心が無いといってもいいくらいなので、質問するよりもまずは計測することをお勧めします。そのうえで、普通はもっとも時間のかかっている箇所(ボトルネック)について改善案を考えるか、あるいはそもそも改善する価値があるのかどうかも含め検討します。

UNIX哲学より

ルール2: 計測すべし。計測するまでは速度のための調整をしてはならない。

投稿2016/12/16 05:14

sharow

総合スコア1149

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takey

2016/12/16 06:22

至言ですね。 まったくおっしゃるとおりです。
guest

0

正直な所どっちが速いかはわかりません。
ただ変数の参照程度の処理なら、何百万回と参照してようやく差が出てくる程度じゃないでしょうか。
処理速度を上げたいのであれば、もっと他の所を見直した方が良いと思いますよ。
ポインタにこだわって可読性が落ちる方が問題かと思います。

投稿2016/12/16 02:23

ttyp03

総合スコア16998

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

maisumakunさんのコメントをみて自分の回答は正しくないと気づきました。
コードがすっきりするのは(B)ですが早いのは(A)かもしれません。
a.b.c.member ...(A)
p=&a.b.c; p->member ...(B)

下記は取り消します。失礼しました。


「一般的には構造体の先頭アドレスを指すポインターを介してアクセスするのが一番早い」と考えてよいと思います。

p->member

はおおまかに次のようなコードになるでしょう

スタックからpの内容をregisterへロード...(1)
registerの相対アクセスでmemberへアクセス

コンパイラーの最適化によってはpのアドレスが既にregisterに入っているなら(1)は省略されてしまうこともあるかも知れません。そうならなくても(1)のアクセスはスタック上へのアクセスになるのでキャッシュヒットしている可能性が高くメモリーへアクセスしないでロードができる期待が高くなるといえましょう。

これはa->b->c.memberなどに比べて早いというのは想像できると思います。なぜなら複数のCのメンバーへアクセスする度にポインターaの中身をロードしたりbの中身をロードするからです。しかしご質問はa.b.c.memberと書いたときの比較ですね。同じスピードで動く機械語が生成されるかも知れませんし、そうならないかも知れません。しかし一般的にいえば同一の構造体内の複数のメンバーに連続してアクセスするならポインターを介してアクセスすることの方が早いと考えてよいと思います。

細かい性能の違いが気になる場合はアセンブリーソースを比較してみるのがより明解だと思います。ただ高性能なCPUは前の命令をやりながら次の命令も並行してやってたりするので機械語の羅列を見ただけでは即座にどちらが早いというのは難しかもしれません。こういった単純なコーディングの違いでも性能に差がでる要因にはいろいろあると思うので実際に性能を図ってみた方が確実だと思います。

投稿2016/12/16 02:19

編集2016/12/16 02:27
KSwordOfHaste

総合スコア18394

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問