64ビットプログラムを書くときのソースコードの実際の違いは何ですか? たとえば、アセンブリのみが異なりますか?C++ の 64 ビット バージョンがあるようではありません。それがコンパイラのオプションと同じくらい単純なものである場合、なぜ多くのプログラムがネイティブに 64 ビットではないのでしょうか? 32ビットCPUと64ビットCPUの唯一の違いがレジスタサイズである場合、それがプログラムにどのように影響するかわかりません(より多くのメモリをアドレス指定できることは別として)。
5 に答える
32ビット/64ビット移植の一般的な落とし穴は次のとおりです。
プログラマーによる暗黙の仮定
sizeof(void*) == 4 * sizeof(char)
。
この仮定を行い、たとえばそのように配列を割り当てる場合(「20個のポインターが必要なので、80バイトを割り当てる」)、バッファーオーバーランが発生するため、コードは64ビットで中断します。「子猫キラー」(@ SK-logic)、
int x = (int)&something;
(およびその逆void* ptr = (void*)some_int
)。再びの仮定sizeof(int) == sizeof(void*)
。これによりオーバーフローは発生しませんが、データが失われます。つまり、ポインタの上位32ビットです。
これらの問題は両方ともタイプエイリアシングと呼ばれるクラスであり(2つのタイプ間のバイナリ表現レベルでの同一性/互換性/同等性を前提としています)、そのような前提は一般的です。UN * Xのように、、、time_t
でsize_t
あるoff_t
ことint
、またはWindowsの場合HANDLE
、、void*
およびlong
互換性があることなどを想定しています。データ構造/スタックスペースの使用に関する仮定(以下の5も参照)。C / C ++コードでは、ローカル変数はスタックに割り当てられ、そこで使用されるスペースは、以下の点と、引数を渡すための異なるルール(通常、スタック上の32ビットx86、64ビット)により、32ビットモードと64ビットモードで異なります。一部レジスタにx86)。32ビットのデフォルトのスタックサイズをほぼ回避するコードは、64ビットでスタックオーバーフローのクラッシュを引き起こす可能性があります。
これはクラッシュの原因として比較的簡単に特定できますが、アプリケーションの構成可能性によっては修正が難しい場合があります。32ビットコードと64ビットコードのタイミングの違い(コードサイズ/キャッシュフットプリントの違い、メモリアクセス特性/パターンの違い、または呼び出し規約の違いによる)は、「キャリブレーション」を破る可能性があります。たとえば、
for (int i = 0; i < 1000000; ++i) sleep(0);
32ビットと64ビットではタイミングが異なる可能性があります...最後に、ABI(アプリケーションバイナリインターフェイス)。通常、64ビット環境と32ビット環境の間にはポインタのサイズよりも大きな違いがあります...
現在、64ビット環境の2つの主要な「ブランチ」であるIL32P64(Win64が使用するもの-int
でlong
あり、 /int32_t
のみであり、)およびLP64(UN * Xが使用するもの-is 、is 、および/ is )ですが、さまざまなアライメントルールの「細分化」もあります-一部の環境では、、またはuintptr_t
void*
uint64_t
<stdint.h>
int
int32_t
long
int64_t
uintptr_t
void*
uint64_t
long
float
double
それぞれのサイズで整列しますが、他の人は4バイトの倍数で整列すると想定します。32ビットLinuxでは、すべて4バイトで整列しますが、64ビットLinuxでは、4バイトおよび8バイトの倍数でfloat
整列します。 これらのルールの結果は、多くの場合、データ型宣言が完全に同一であっても、構造体/クラスメンバーのビットとオフセットが32ビット環境と64ビット環境で異なることです。 配列/ベクトルの割り当てに影響を与えるだけでなく、これらの問題は、たとえばファイルを介したデータの入出力にも影響を与えます-32ビットアプリが、同じアプリが64ビットの読み込み用に再コンパイルしたファイルに書き込む場合、結果は期待どおりではありません。上記の例は、言語プリミティブ に関するもののみです。long
double
sizeof(struct { ...})
struct { char a; int b; char c, long d; double e }
(char
、、など)しかし、もちろんint
、プラットフォームに依存する/ランタイムライブラリのあらゆるlong
種類のデータ型に影響します。size_t
off_t
time_t
HANDLE
struct
union
class
そして、下位レベルの違いがあります。これは、たとえば、手動で最適化されたアセンブリ(SSE / SSE2 / ...)の場合に関係します。32ビットと64ビットには、異なる(数の)レジスタ、異なる引数受け渡しルールがあります。これらはすべて、このような最適化の実行方法に大きく影響します。たとえば、32ビットモードで最高のパフォーマンスを発揮するSSE2コードは、64ビットモードで最高のパフォーマンスを発揮するように書き直す/拡張する必要があります。
32ビットと64ビットでは、特にメモリの割り当て/管理に関して、コード設計の制約も大きく異なります。「32ビットで取得できるメモリを最大限に活用する」ように注意深くコーディングされたアプリケーションには、メモリの割り当て/解放、メモリマップトファイルの使用、内部キャッシュなどに関する複雑なロジックがあります。利用可能な巨大なアドレス空間を「単に」利用できる64ビットでは有害です。このようなアプリは64ビットで問題なく再コンパイルされる可能性がありますが、最大化32ビットのぞき穴最適化がすべて行われていなかった「古代の単純な非推奨バージョン」よりもパフォーマンスが低下します。
つまり、最終的には、機能強化/ゲインについても重要であり、プログラミング、設計/要件の一部でより多くの作業が必要になります。アプリが32ビット環境と64ビット環境の両方でクリーンに再コンパイルされ、両方で検証されたとしても、実際には64ビットの恩恵を受けていますか?64ビットでより多くのことを実行/より高速に実行するためにコードロジックに実行できる/実行する必要のある変更はありますか?32ビットの下位互換性を損なうことなくこれらの変更を行うことができますか?32ビットターゲットに悪影響を与えませんか?強化はどこにあり、どれだけ得ることができますか?
大規模な商業プロジェクトの場合、出発点は既存の「お金のメーカー」であるため、これらの質問への回答はロードマップ上の重要なマーカーになることがよくあります...
64 ビット プラットフォームで動作するように、すべてのプログラムを書き直す必要があるわけではありません。より大きなアドレス空間など、一部の 64 ビット OS で利用可能なより大きな自由度を利用するために、プログラムが書き直されることがよくありますが、それはプログラムが機能するために書き直さなければならないという意味ではありません。
一部のプログラムは、ソース コードを変更せずに再コンパイルする必要がある場合があります。中間表現とジャストインタイム コンパイルを使用する一部のプログラミング環境では、32 ビットまたは 64 ビット OS で同じバイナリを変更せずに実行できます。
ネイティブプログラムを書き直す必要がある場合、他の回答が述べているように、通常はポインターサイズに関する仮定が原因です。一部の人々は、これらの仮定をバグと呼ぶかもしれません。
これらにはさまざまなメモリ アラインメントの問題があり、多くの初心者プログラマーはすべて 32 ビット (ポインター サイズ、ファイル オフセット、タイムスタンプ サイズなど) を前提としています。
コンパイルされた言語で正しく書かれたプログラムの場合、実行する必要があるのはそれを再コンパイルすることだけです。「書き直す」(または変更する) 必要がある唯一のプログラムは、最初から不十分に書かれており、多くの偽りの仮定を行っており、おそらく野生のような未定義の動作を呼び出しているプログラムです。
マシン コードにコンパイルされていないが、半解釈/バイトコード/JIT 形式で解釈または実行される言語の場合、32 ビット マシンから 64 ビット マシンに移行したときに壊れるプログラムを作成するのは実際にはかなり困難です。おそらく、それが「壊れる」可能性が最も高い方法は、より多くのメモリを使用し、おそらく不足することです。
ポインターは通常、64 ビット システムでは 64 ビットです。