c言語での基本データ型で知りたいことがあります。
#質問1
1.signed long
2.unsigned long
3.signed char
4.unsigned long
私の環境でこの4つの型でシフト演算をし時間を計測したところ、列挙した順に速かったのです。
signed と unsigned ではなぜ計算速度に差が出るのか、そしてなぜ1byteのcharより8byteのlongの方が速いのかということに疑問を抱きました。
8byteの型はメモリを8つ連番で使用していると認識していますが、内部でどのように計算してるかはよく知りませんが、1byteのcharの方が速そうなもんです。
#質問2
私が構想を練っているソフトでは、56byteのデータを扱いたいのですが、intやlongより大きいそのような型を定義することはできますでしょうか。
具体的には、将棋のAIを開発しようと思っていて、局面を表すデータ型として使用しようと思っています。上の方法が無理だったり、できたとしてもあまり実用的でないのなら普通に構造体と配列を使いますが、知識として知っておきたいというのもあります。
使用しているコンパイラはgcc ver4.2.1です。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
signed/unsignedの違いについてコメントします。
失礼ながら測定誤差の可能性はないでしょうか?
きちんとデータシートを見たわけではないので単なる想像でしかありませんがこの2種類の命令は大抵のプロセッサで同一の性能ではないかなぁと思います。
自分のPCはIA64ですがgccで最適化なしでコンパイルし、アセンブラソースで期待外の最適化が行われていないことを確認した上で100M回のシフトによる測定をsigned/unsignedで交互に行い、それを100回やって平均・標準偏差をみると以下のようになりました。(clock関数による結果)
平均 標準偏差
194.70 9.96 singed
193.73 10.14 unsigned
この結果から自分は「どちらが早いとは言えない」と思いました。こんなことをしなくても本来はプロセッサのデータシートを調べるべきなのでしょうが・・・
もし上記よりおおざっぱな測定をしていたとしたら、本当に「違うかどうか」より注意深く測定して「違う」という確証を得てから「なぜ違うか」を考えた方がよいのではないかと思います。もし充分な測定を行った上での質問だったとしたら大変失礼なコメントになってしまいますが、その際はご容赦を。
(なお、char/longについてはChironianさんがおっしゃるように違いがあっても不思議ではないと思いました。)
投稿2017/03/01 15:13
編集2017/03/03 02:29総合スコア18392
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
アーキテクチャがRISCかCISCか、また、CISCでも最近のCPUは内部がRISCなのでCPUが何かによって違いが出ると思います。
ひとまず、機械語レベルでの違いを見てみました。環境はmacOS SierraでCore i7(x86-64)です。
C
1signed char sc(signed char x) 2{ 3 return x >> 1; 4} 5unsigned char uc(unsigned char x) 6{ 7 return x >> 1; 8} 9signed long sl(signed long x) 10{ 11 return x >> 1; 12} 13unsigned long ul(unsigned long x) 14{ 15 return x >> 1; 16}
を、gcc-6(GCC 6.3.0)で-O0
(最適化無し)でコンパイルし、gobjdumpで逆アセンブリした結果は下記になります。
0000000100000dd8 <_sc>: 100000dd8: 55 push %rbp 100000dd9: 48 89 e5 mov %rsp,%rbp 100000ddc: 89 f8 mov %edi,%eax 100000dde: 88 45 fc mov %al,-0x4(%rbp) 100000de1: 0f b6 45 fc movzbl -0x4(%rbp),%eax 100000de5: d0 f8 sar %al 100000de7: 5d pop %rbp 100000de8: c3 retq 0000000100000de9 <_uc>: 100000de9: 55 push %rbp 100000dea: 48 89 e5 mov %rsp,%rbp 100000ded: 89 f8 mov %edi,%eax 100000def: 88 45 fc mov %al,-0x4(%rbp) 100000df2: 0f b6 45 fc movzbl -0x4(%rbp),%eax 100000df6: d0 e8 shr %al 100000df8: 5d pop %rbp 100000df9: c3 retq 0000000100000dfa <_sl>: 100000dfa: 55 push %rbp 100000dfb: 48 89 e5 mov %rsp,%rbp 100000dfe: 48 89 7d f8 mov %rdi,-0x8(%rbp) 100000e02: 48 8b 45 f8 mov -0x8(%rbp),%rax 100000e06: 48 d1 f8 sar %rax 100000e09: 5d pop %rbp 100000e0a: c3 retq 0000000100000e0b <_ul>: 100000e0b: 55 push %rbp 100000e0c: 48 89 e5 mov %rsp,%rbp 100000e0f: 48 89 7d f8 mov %rdi,-0x8(%rbp) 100000e13: 48 8b 45 f8 mov -0x8(%rbp),%rax 100000e17: 48 d1 e8 shr %rax 100000e1a: 5d pop %rbp 100000e1b: c3 retq
charの方が命令が多いのがわかると思います。そもそもC言語では、int以下の大きさの整数型を演算する場合はint型に変換してから演算します。最適化せずにバカ正直に処理した場合、int型への変換分だけ必ず遅くなります。signedとunsignedの違いはsar
とshr
の違いだけですが、CPUによって異なると思うのでなんとも言えません。x86-64では1命令=1クロックとは限らないからです。
ただし、これを-O2
で最適化するとまた変わってきます。
0000000100000e70 <_sc>: 100000e70: 89 f8 mov %edi,%eax 100000e72: d0 f8 sar %al 100000e74: c3 retq 100000e75: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1) 100000e7c: 00 00 00 00 0000000100000e80 <_uc>: 100000e80: 89 f8 mov %edi,%eax 100000e82: d0 e8 shr %al 100000e84: c3 retq 100000e85: 66 66 2e 0f 1f 84 00 data16 nopw %cs:0x0(%rax,%rax,1) 100000e8c: 00 00 00 00 0000000100000e90 <_sl>: 100000e90: 48 89 f8 mov %rdi,%rax 100000e93: 48 d1 f8 sar %rax 100000e96: c3 retq 100000e97: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1) 100000e9e: 00 00 0000000100000ea0 <_ul>: 100000ea0: 48 89 f8 mov %rdi,%rax 100000ea3: 48 d1 e8 shr %rax 100000ea6: c3 retq
retqまでの処理は全て同じです。nopw
はレジスタの後処理らしいですが、x86-64にそこまで詳しくないので、どう影響しているかまではよくわかりせん。
投稿2017/03/05 01:09
総合スコア21733
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
こんにちは。
signed と unsigned ではなぜ計算速度に差が出るのか、
不思議ですね。お使いのCPUがsignedのシフト命令を持っているけどunsignedのシフト命令を持っていない時は有り得そうです。右シフトについてはsignedは符号拡張し、unsignedは符号ビットに0を入れます。通常はどちらも1命令で終わります。
そしてなぜ1byteのcharより8byteのlongの方が速いのかということに疑問を抱きました。
例えば、64ビットCPUならlong処理は1命令で済みます。
しかし、1バイト処理する場合、longに対してシフトして目的の1バイト以外の部分を元の値にしたlongへ組み立ててストアするので手間がかかるケースが多いです。
私が構想を練っているソフトでは、56byteのデータを扱いたいのですが、intやlongより大きいそのような型を定義することはできますでしょうか。
構造体を使えば良いと思います。
投稿2017/03/01 14:07
総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/03/01 14:45
2017/03/01 15:22
0
質問1について、 gcc を使っているのであれば、
gcc -S source.c の様に -S オプションを付けてコンパイルすると、
source.s というアセンブラ言語の中間ソースが出力されます。
その、source.s の中を見て、アセンブラの命令を調べれば、
具体的に何をやっているか分かります。
質問2については、昔自分も仕事で大きな数字を使う必要があり、
コンパイル時に決定する必要はありますが、いくらでも大きな整数
を扱える処理を作った事があります。
変数の格納には、構造体を使いました。
投稿2017/03/02 13:57
総合スコア241
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。