[解決済み] なぜ、型はその値に関係なく、常に一定の大きさなのですか?
質問
実装によって型の大きさは異なりますが、unsigned intやfloatのような型は常に4バイトです。しかし、なぜある型が常に ある は、どのような値であってもメモリ量に影響を与えるのでしょうか?例えば、次のような255という値を持つ整数を作ったとします。
int myInt = 255;
次に
myInt
は、私のコンパイラでは4バイトを占有することになります。しかし、実際の値では
255
は1バイトで表現できるのに、なぜ
myInt
は、1バイトのメモリを占有するだけではないのでしょうか?あるいは、もっと一般化した聞き方。値を表現するのに必要な領域はそのサイズよりも小さいかもしれないのに、なぜ型には1つのサイズしか関連付けられていないのか?
どうすれば解決するの?
コンパイラは、あるマシン用のアセンブラ(最終的にはマシンコード)を生成することになっており、一般にC++はそのマシンに同調しようとする。
基礎となるマシンに共感するということは、大まかに言って、マシンが高速に実行できる操作に効率的にマッピングするC++コードを書きやすくするということです。つまり、私たちは、私たちのハードウェアプラットフォームで高速かつ自然なデータ型と操作へのアクセスを提供したいのです。
具体的に、あるマシン・アーキテクチャを考えてみましょう。現在のIntel x86ファミリーを例に挙げてみましょう。
インテル® 64およびIA-32アーキテクチャー・ソフトウェア開発者向けマニュアル vol1 ( リンク )の3.4.1項にはこう書かれています。
32ビット汎用レジスタEAX, EBX, ECX, EDX, ESI、EDI、EBP、およびESPは、以下の情報を保持するために用意されています。 以下の項目があります。
- 論理演算、算術演算のオペランド
- アドレス計算のためのオペランド
- メモリポインタ
そこで、コンパイラがC++の単純な整数演算をコンパイルする際に、これらのEAX、EBXなどのレジスタを使用するようにしたいのです。これはつまり、私が
int
これらのレジスタを効率的に使用できるように、これらのレジスタと互換性のあるものである必要があります。
レジスタは常に同じサイズ(ここでは32ビット)であるため、私の
int
変数も常に32ビットになります。変数の値をレジスタにロードしたり、レジスタを変数にストアしたりするたびに変換する必要がないように、同じレイアウト(リトルエンディアン)を使用することにします。
使用方法 ゴッドボルト を使えば、コンパイラがどのような処理をしているかがわかります。
int square(int num) {
return num * num;
}
はコンパイルします(GCC 8.1および
-fomit-frame-pointer -O3
を簡略化しています)。
square(int):
imul edi, edi
mov eax, edi
ret
という意味です。
-
その
int num
パラメータはレジスタ EDI で渡され、Intel がネイティブ・レジスタに期待するサイズとレイアウトを正確に表しています。この関数は何も変換する必要がありません。 -
乗算は1つの命令(
imul
)であり、非常に高速です。 - 結果を返すのは、単に他のレジスタにコピーする問題です(呼び出し側は結果がEAXに置かれることを期待しています)。
編集:非ネイティブレイアウトを使用した場合の違いを示すために、関連する比較を追加することができます。最も単純なケースは、ネイティブ幅以外のもので値を保存することです。
使用方法 ゴッドボルト 再び、単純なネイティブの乗算を比較します
unsigned mult (unsigned x, unsigned y)
{
return x*y;
}
mult(unsigned int, unsigned int):
mov eax, edi
imul eax, esi
ret
を、非標準の幅に対応する同等のコードに置き換えたものです。
struct pair {
unsigned x : 31;
unsigned y : 31;
};
unsigned mult (pair p)
{
return p.x*p.y;
}
mult(pair):
mov eax, edi
shr rdi, 32
and eax, 2147483647
and edi, 2147483647
imul eax, edi
ret
余分な命令はすべて、入力フォーマット(2つの31ビット符号なし整数)を、プロセッサがネイティブに扱えるフォーマットに変換することに関係しています。もし結果を31ビットの値に戻したいなら、そのための命令がもう1つか2つ必要になります。
このように余計に複雑になるということは、スペースの節約が非常に重要な場合にのみ、これを気にすることになります。この場合、ネイティブの
unsigned
または
uint32_t
という型があれば、もっとシンプルなコードが生成されるはずです。
ダイナミックサイズについての注意点。
上の例は、可変幅ではなく固定幅の値のままですが、幅(とアライメント)がネイティブレジスタと一致しなくなりました。
x86プラットフォームには、メインの32ビット以外に8ビットや16ビットなど、いくつかのネイティブサイズがあります(わかりやすくするため、64ビットモードやその他いろいろなことは割愛します)。
これらの型(char、int8_t、uint8_t、int16_tなど)は また これは、古い 8086/286/386/ 等の命令セットとの後方互換性を確保するためでもあります。
確かに、最小の 自然な固定サイズ シングル命令でロードとストアをすばやく実行でき、フルスピードのネイティブ演算が可能で、キャッシュミスを減らすことによってパフォーマンスを向上させることもできます。
これは可変長エンコーディングとは全く異なるもので、私もいくつか扱ったことがありますが、ひどいものでした。ロードするたびに1つの命令ではなく、ループになります。すべてのストアはループになります。すべての構造体が可変長であるため、当然ながら配列は使えません。
効率化に関するさらなる注意点
その後のコメントで、ストレージサイズに関して、私の知る限りでは「quot;efficient"」という言葉を使っていますね。非常に多くの値をファイルに保存したり、ネットワーク経由で送信したりする場合には、ストレージサイズを最小化することが重要になることがあります。そのトレードオフとして、これらの値をレジスタにロードして する 変換は無料ではありません。
効率について議論するときは、何を最適化しているのか、トレードオフの関係はどうなっているのかを知る必要があります。非ネイティブのストレージタイプを使用することは、処理速度とスペースを交換する方法の1つであり、時には理にかなっています。可変長ストレージの使用は(少なくとも算術型については)、以下のようなトレードオフをもたらします。 より 処理速度(およびコードの複雑さと開発者の時間)は、多くの場合、さらに最小限のスペースの節約になります。
このために支払うスピードのペナルティは、帯域幅や長期保存を絶対に最小化する必要がある場合にのみ価値があるということです。そのような場合には、通常はシンプルで自然なフォーマットを使用して、汎用システム(zip、gzip、bzip2、xyなど)で圧縮する方が簡単です。
tl;dr
各プラットフォームには1つのアーキテクチャがありますが、データを表現する方法は基本的に無限に思いつくことができます。どの言語でも、組み込みのデータ型を無制限に提供するのは合理的ではありません。そこでC++は、プラットフォームが本来持っている自然なデータ型の集合に暗黙的にアクセスできるようにし、それ以外の(ネイティブではない)表現を自分でコーディングできるようにしています。
関連
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み】IntelliSense:オブジェクトに、メンバー関数と互換性のない型修飾子がある
-
[解決済み】デバッグアサーションに失敗しました。C++のベクトル添え字が範囲外
-
[解決済み] gdbを使用してもデバッグシンボルが見つからない
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み] スタックアロケーションにより初期化されていない値が作成された
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] C++のPOD型とは何ですか?
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み】関数名の前に期待されるイニシャライザー
-
[解決済み】cc1plus:エラー:g++で認識されないコマンドラインオプション"-std=c++11"
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】リンカーエラーです。"リンカ入力ファイルはリンクが行われていないため未使用"、そのファイル内の関数への未定義参照
-
[解決済み] 非静的データメンバの無効な使用
-
[解決済み】CMakeエラー at CMakeLists.txt:30 (project)。CMAKE_C_COMPILER が見つかりませんでした。
-
[解決済み] 解決済み] `pthread_create' への未定義の参照 [重複] [重複
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。