

doubleを-malign-doubleオプションで8バイトアラインメントにする具体的なメリットはなんでしょうか？


CC BY-SA 4.0
2021-12-06T22:12:25.907
83984
2023-05-04T07:15:33.817
2023-05-04T07:15:33.817
4236
37013
question
linux windows c gcc x86
doubleを-malign-doubleオプションで8バイトアラインメントにするメリットはなんでしょうか？


[gcc マニュアル x86](https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html) によると

  * `double` `long double` `long long` を３２ビット境界に載せるか６４ビット境界に載せるかが変わる
  * Pentium 等では少し高速化する（少量のメモリの犠牲と引き換えに）
  * `x86-64` ではデフォルトで有効

とあります。最後の項は微妙に不正確というか `x86-64` では常に `-malign-double` が有効で `-mno-align-double`
は無効化されています。

で、何が嬉しいのかというと、その辺はハードウエア仕様を知らないと理解しがたいです。厳密さに目をつぶっておおまかな話をすると

  * 32bit CPU というのはバス幅が 32bit です。よって１回のバスアクセスで 32bit の値を読み書きすることができます。つまり `double` １つの読み書きには２回のバスアクセスが必要です。
  * 64bit CPU というのはバス幅が 64bit です。よって１回のバスアクセスで 64bit の値を読み書きすることができます。つまり `double` １つの読み書きには１回のバスアクセスが必要です。

上記の話が成立するのは整合がとれている場合に限ります。 64bit な値つまり `double` が 64bit
に整合していないと、バスアクセスが２回必要になるのは理解できるでしょうか？図示するとわかりやすいんですが AA 下手なので略。

というわけで「整合をとると、ハードウエア的にバスアクセス回数が減るので高速化できる」というのが答えとなります。

`-malign-double` なオブジェクトと `-mno-align-double` なオブジェクトは (x86 32bit では) `struct`
等の整合が違ってしまい非互換になるので混ぜるな危険です。

* * *

なんかうまく説明しきっていない気がしたので追記

32bit CPU と 32bit OS/アプリの組み合わせでは `-malign-double`
を指定してもしなくても速度に一切変化がない（バスアクセス回数に変わりはない）わけです。混ぜるな危険の分、指定しないほうが無難そうです。

64bit CPU と 32bit OS/アプリの場合のみ、バスアクセス回数が１減る可能性が高まり（ `-malign-double` を指定していなくても
64bit 境界に載っていることは普通にあるので常に効果があるわけではない）その分高速になる、のでしょうが x86 では L1 cache
のアクセス回数が１減っても体感できるほどの差は出ません。

64bit CPU と 64bit OS/アプリの場合は最初から `-malign-double` は有効で、無効化できませんのでそもそも違いがないし。

ということで体感できる差が出るとしたら

  * ハード的には 64bit CPU が普及したがソフトはまだ 32bit が主流な短期間だけ
  * `double` の演算をひたすら繰り返す数値解析ソフト (32bit) を使う

時くらいです（真の逸般人ならソフトも即 64bit に移行しているはずなので、この状況に該当するとしたらえせ逸般人であろう）。

一般人が一般的な使い方する分には目に見えるほどの速度向上効果は望めず、メモリ消費量だけ微妙に増え、ということでメリットはあまり体験できず、 `struct`
が ABI
非互換になるデメリットだけありそうです。コンパイラ実装者は当然その辺全部理解しているので、デフォルトで有効になっていないコンパイルオプションにはそれなりの理由がきっちりあります。それをあえて我々末端ユーザーが指定するときにはリスクとメリットを理解したうえで行うことが大事です。


CC BY-SA 4.0
2021-12-07T00:07:14.520
83985
2021-12-07T11:53:22.960
2021-12-07T11:53:22.960
8589
8589
83984
answer


774RRさんが匂わせているように、多少古い話題なようです。

## Intelの見解

SSE2などが登場する以前ですが[インテル・アーキテクチャ最適化マニュアル（1997年）](https://www.intel.com/content/dam/www/public/ijkk/jp/ja/documents/developer/ia_opti.pdf#page=41)に

> ### 3.4.2 データ
>
> Pentium プロセッサでは、データ・キャッシュ内またはバス上でアライメントが合わないアクセスを行 うと、最低 3
> クロック・サイクルの余分なコストを要する。Pentium Pro および Pentium II
> プロセッサでは、データ・キャッシュ内でアライメントが合わない (キャッシュ・ライン境界にまたがる )アクセスを行 うと、9 ～ 12
> クロック・サイクルのコストが生じる。どのプロセッサ上でも最高の実行パフォーマンスが得られるよう、データのアライメントを以下のガイドラインに従って境界に合わせるようお勧めする。
>
>   * 8 ビット・データは任意の境界にアライメントを合わせる。
>   * 16 ビット・データはアライメントが合った 4 バイト・ワード内に収まるようにアライメントを合わせる。
>   * 32 ビット・データは 4 バイトの整数倍の任意の境界にアライメントを合わせる。
>   * 64 ビット・データは 8 バイトの整数倍の任意の境界にアライメントを合わせる。
>   * 80 ビット・データは 128 ビット境界 (すなわち、16 バイトの整数倍の任意の境界 )にアライメントを合わせる。
>

とあり、適切なパフォーマンスを得るためには`double`は8バイトアラインとすることを推奨しています。同書には

> ### 3.5.1.5 メモリ内およびスタック上のデータのアライメント合わせ
>
> Pentium プロセッサでは、8 バイト境界にアライメントが合っていない 64 ビット変数にアクセスすると、3
> サイクルの余分なコストを要する。Pentium Pro および Pentium IIプロセッサでは、そのような変数が 32
> バイト・キャッシュ・ライン境界にまたがっていると、D C U スプ
> リットを生じることがある。市場に出回っているコンパイラのなかには、倍精度データのアライメントを 8 バイト境界に合わせないものもある。

と愚痴のような記述も見られます。

## LinuxなどUNIX全般の見解

Linuxなどが参照している[SYSTEM V APPLICATION BINARY INTERFACE Intel386 Architecture
Processor SupplementのFundamental
Types](https://refspecs.linuxbase.org/elf/abi386-4.pdf#page=28)には標準仕様として
`double` は 4バイトアラインとすると記されています。その下には

> The Intel386 architecture does not require doubleword alignment for
> doubleprecision values. Nevertheless, for data structure compatibility with
> other Intel architectures, compilers may provide a method to align double-
> precision values on doubleword boundaries.

と書かれていて、8バイトアラインされることも知ってはいるようです。

## gccの見解

というわけで、gccとしてはプラットフォーム仕様に従い、4バイトアラインとしつつも、適切なパフォーマンスを得るための選択肢として8バイトアラインさせる`-malign-
double`を用意している、ということだと思います。

## Windowsの見解

ちなみにWindowsでは[/Zp (Struct Member Alignment)](https://docs.microsoft.com/en-
us/cpp/build/reference/zp-struct-member-alignment?view=msvc-170)で

  * x86、ARM、ARM64は8バイトアライン
  * x64は16バイトアライン

としていて、Intelの推奨に沿っていて、`-malign-double`相当が既定値です。

## 最近の動向

SSE命令の登場で状況は一変しています。以前の命令と異なり、SSE命令ではデータアラインが必須になっています。例えばSSEは128bitデータを扱うため、128bitすなわち16バイトアラインする必要があります。  
そのため、[インテル 64 アーキテクチャー および IA-32 アーキテクチャー
最適化リファレンス・マニュアル（2011年）](https://www.intel.co.jp/content/dam/www/public/ijkk/jp/ja/documents/developer/248966-024JA.pdf)などを見ても、8バイトアラインの話題は消え去っています。


CC BY-SA 4.0
2021-12-07T14:37:53.287
84011
2021-12-07T14:37:53.287
4236
83984
answer