10    プログラムの最適化

アプリケーションの最適化には,作成プロセスの変更,ソース・コードの変更,またはその両方を含みます。

多くの場合,アプリケーションを最適化すると,主に実行時性能が改善されます。 ただし,アプリケーション・プログラムの実行時性能を計測して,性能の改善方法を分析する前に,次の 2 つの点について確認してください。

これらの事項について確認してから,最適化処理を開始してください。

アプリケーションの最適化プロセスは,補完的ではあるが,2 つの別個のアクティビティに分けることができます。

以降の各節で,チューニング・プロセスのこれら 2 つのアスペクトに関して詳細に説明します。

10.1    アプリケーション・プログラム作成のガイドライン

アプリケーションの実行時性能の自動的な改善は,作成プロセスのあらゆるフェーズにおいて行うことができます。 以降の項で,コンパイル,リンクとロード,前処理と後処理,および,ライブラリ選択において行うことができる主な改善について説明します。 特に効果のある手法は,spike ツールを用いたプロファイル主導の最適化です (10.1.3 項)。

10.1.1    コンパイルに関する考慮事項

アプリケーションのコンパイルを,設定できる最高の最適化レベル,つまり,最高の性能と正しい結果が得られるレベルで行います。 一般に,言語使用の標準に準拠しているアプリケーションは,最高の最適化レベルでコンパイルすることができますが,そのような標準に準拠していないアプリケーションは,低い最適化レベルでコンパイルしなければならないことがあります。 詳細については, cc(1),または第 2 章を参照してください。

アプリケーションを最高のレベルでコンパイルできる場合には,すべてのソース・ファイルを 1 回のコンパイルで一緒にコンパイルしてください。 複数のソース・ファイルをコンパイルすると,コンパイラが最適化のために調査するコードの量が増加します。 これにより,次のような効果が得られます。

これらの最適化を行うには,コンパイル・オプションの -ifo-O3 または -O4 を使用します。

最高レベルの最適化が特定のプログラムで効果があるかどうかを判断するには,プログラムを 2 回コンパイルして,それぞれの結果を比較します。 1 回目は,最高の最適化レベルでコンパイルし,2 回目には,1 つ下の最適化レベルでコンパイルします。 いくつかのルーチンは最高の最適化レベルでコンパイルできないことがあるため,そのようなルーチンは別々にコンパイルする必要があります。

実行時性能に重大な影響を及ぼす可能性があるその他のコンパイル時の考慮事項には,次の事柄があります。

オプション 説明
-arch 命令を生成する Alpha アーキテクチャのバージョンを指定する。 -arch と -tune の違いについては, cc(1) の -arch を参照。
-ansi_alias ソース・コードが ANSI C の別名化規則に準拠しているかどうかを指定する。 ANSI C 別名化規則では,よりアグレッシブな最適化が許可されている。
-ansi_args ソース・コードが引数に関して ANSI C の規則に準拠しているかどうかを指定する。 ANSI C の規則に準拠している場合には,特別な引数クリーニング・コードを生成する必要がない。
-fast

性能向上のため,次の最適化オプションをオンにする。

-ansi_alias
-ansi_args
-assume trusted_short_alignment
-D_FASTMATH
-float
-fp_reorder
-ifo
-D_INLINE_INTRINSICS
-D_INTRINSICS
-intrinsics
-O3
-readonly_strings

-feedback 最適化の際に,コンパイラが指定されたファイル内のプロファイル情報を使用することを指定する。 詳細については,10.1.3.2 項を参照。
-fp_reorder 浮動小数点演算に影響する特定のコード変換が許可されるかどうかを指定する。
-G スモール・データ・セクション(sbss または sdata) におけるデータ項目の最大バイト・サイズを指定する。
-inline 関数のインライン展開を実行するかどうかを指定する。
-ifo ファイルが別々にコンパイルされた場合,可能な限り,ファイル境界を超えて,最適化の向上 (ファイル間の最適化) とコード生成を行う。
-O コンパイルにより到達可能な最適化レベルを指定する。
-om さまざまなポストリンク・コード最適化を実行する。 -non_shared オプションを用いてコンパイルしたプログラムで,最も効果がある (付録 F を参照)。 このオプションは,-spike オプションに置き換えられている (10.1.3 項 を参照)。
-preempt_module モジュールごとのシンボルの優先使用をサポートする。
-speculate 実行パスが取られる前に,その実行パスで実行中のプログラム内で動作 (たとえば,ロードや演算操作) が行われるようにする。
-spike さまざまなポストリンク・コード最適化を実行する (10.1.3 項 を参照)。
-tune Alpha アーキテクチャ固有の処理系に対して,プロセッサ固有の命令のチューニングを選択する。 -tune と -arch の違いについては, cc(1) の -arch を参照。
-unroll レベル -O2 以上の最適化プログラムで行われるループの展開を制御する

上記のオプションを使用すると,正確さと標準に対する準拠が損なわれることがあります。

10.1.2    リンクおよびロードに関する考慮事項

アプリケーションで多数の大きなライブラリを使用しない場合には,それを共用しないでリンクすることを考慮してください。 このようにすると,リンカがライブラリへの呼び出しを最適化できるため,(呼び出しが頻繁に行われる場合には) アプリケーションの起動時間が短縮されて,実行時性能が改善されます。 ただし,共用されないアプリケーションは呼び出し共用のアプリケーションより多くのシステム資源を使用することがあります。 多数のアプリケーションを同時に実行しているとき,アプリケーションに共通なライブラリのセットがある (たとえば,libX11libc) 場合には,それらのライブラリを呼び出し共用としてリンクすると,システム全体の性能を向上させることができます。 詳細については,第 4 章を参照してください。

シェアード・ライブラリを使用するアプリケーションでは,それらのライブラリがクイックスタートできることを確認してください。 クイックスタートは,アプリケーションのロード時間を大幅に減少させる Tru64 UNIX の機能です。 アプリケーションの数が多い場合には,アプリケーションの起動と実行に必要な全時間のうち,かなりの割合をロード時間が占めます。 オブジェクトをクイックスタートできない場合には,実行はできますが,起動時間が遅くなります。 詳細については,4.7 節を参照してください。

10.1.3    spike およびプロファイル主導の最適化

この項では,ポストリンク最適化プログラム spike について説明します。

10.1.3.1    spike の概要

spike ツールは,リンク後のコードの最適化を行います。 プログラム全体を操作できるため,spike ではコンパイラにはできない最適化を行うことができます。 10.1.3.2 項 で述べているように,spike は,最適化を先導するプロファイル情報を使用すると,最も効果があります。

spike は Tru64 UNIX Version 5.1 から提供されるようになったツールであり,om および cord に代わるものです。 制御や最適化の効率が改善されており,実行可能プログラムとシェアード・ライブラリのどちらでも使用できます。 spike は,omcord とは併用できません。 om および cord についての詳細は,付録 F を参照してください。

spike が実行する最適化には,コード・レイアウト,実行されないコードの削除,アドレス計算の最適化などがあります。

spike は,Tru64 UNIX の V4.0 以降のシステムでリンクされたバイナリを処理できます。 V5.1 以降のシステムでリンクされたバイナリには,spike がさらに最適化を行えるようにする情報が含まれています。

注意

spike は,Tru64 UNIX V5.1 以降のイメージに対して一部のアドレス最適化のみを行いますが,om は V4 イメージに対して最適化を行います。 V5.1 より前のバイナリに対して spike を使用し,リンカの最適化を有効にしている (リンクの段階で cc に -O を渡す) 場合,omspike の性能の違いはあまりありません。

spike は,2 通りの方法で使用できます。

この項と 10.1.3.2 項 の例は,spike のこの 2 つの使用方法を示しています。 実行可能ファイルを再リンクしたくない場合 (Example 1) や,コンパイルの後にプロファイル情報を使用する場合 (Example 5 および Example 6) は,spike コマンドが便利です。 プロファイル情報を使用しない場合 (Example 2) や,コンパイラでもプロファイル情報を使用する場合 (Example 3 および Example 4) は,-spike オプションの方が便利です。

Example 1 および Example 2 は,最適化を先導するプロファイル情報を使用せずに spike を使用する方法を示しています。 10.1.3.2 項 では,pixie プロファイラからのフィードバック情報を用いて spike を使用する方法を示しています。

例 1

この例では,spike はバイナリ my_prog に適用され,最適化された出力ファイル prog1.opt を生成します。

% spike my_prog -o prog1.opt

例 2

この例では,cc コマンドの -spike オプションを使って,コンパイル時に spike を適用しています。

% cc -c file1.c
% cc -o prog3 file1.o -spike

最初のコマンド行は,オブジェクト・ファイル file1.o を作成します。 2 番目のコマンド行は file1.o をリンクして実行可能ファイルを作成し,spike を用いて実行可能ファイルを最適化します。

spike コマンドのオプションはすべて,(cc) -WS オプションを用いて,cc コマンドの -spike オプションに直接渡すことができます。 次の例に構文を示します。

% cc -spike -feedback prog -o prog *.c \
     -WS,-splitThresh,.999,-noaggressiveAlign
 
 

spike コマンドのオプションと spike を使用する際の制限についての詳細は, spike(1) を参照してください。

10.1.3.2    プロファイル主導の最適化での spike の使用

自動最適化は,前項で述べた -O,-fast,-inline などのコンパイラの自動最適化オプションを使用すると,ある程度までは達成できます。 これらのオプションは,CPU アーキテクチャとキャッシュ・メモリを最善の方法で使用するための最小限の命令シーケンスを生成する際に役立ちます。

ただし,プログラムが通常の入力データおよび通常の環境のもとで実行された場合に,どの命令が最も多く実行されるかという情報を指定すると,コンパイラとリンカは,この最適化を改善できます。 Tru64 UNIX では,プロファイラの結果を再コンパイルにフィードバックすることで,この情報を指定できます。 このカスタマイズされたプロファイル主導の最適化は,自動最適化と組み合わせて使用できます。

以下の例では,spikepixie プロファイラと併用し,さまざまなフィードバック手法を用いてプログラムから生成される命令シーケンスを調整する方法を示しています。

例 3

この例は,spike を用いたプロファイル主導最適化の基本的な 3 つの手順,すなわち,(1) プログラムの最適化を準備する,(2) 計測機構付きのプログラムを作成して実行し,プロファイリング統計情報を収集する,(3) その情報をコンパイラとリンカにフィードバックして,実行可能コードの最適化に役立てる,という手順を示しています。 後の例では,これらの手順を詳細化して,開発中に行われた変更と複数のプロファイリング実行で得たデータを合わせて調節する方法を示しています。

% cc -feedback prog -o prog -O3 *.c [1]

% pixie -update prog [2]

% cc -feedback prog -o prog -spike -O3 *.c [3]
 
 

  1. プログラムが最初に -feedback オプション付きでコンパイルされたときに,拡張された特別な実行可能ファイルが作成されます。 これには,コンパイラが実行可能ファイルをソース・ファイルに対応させるために使用する情報が含まれています。 また,コンパイラへのプロファイルのフィードバック情報を格納するために後で使用するセクションも含まれています。 このセクションは,最初にコンパイルしたときは,pixie プロファイラ (手順 2) がフィードバック情報をまだ生成していないため,空のままです。 -feedback オプションに指定するファイル名は,必ず実行可能ファイルと同じ名前にしてください。 この例では prog (-o オプションで指定) です。 特に指定しないかぎり,-feedback オプションでは -g1 オプションが適用され,プロファイルに最適なシンボルが付けられます。 -On オプションを試して,対象のプログラムとコンパイラで実行時性能が最高になる最適化のレベルを調べてください。 最初のコンパイルの際には,まだフィードバック情報がないので,コンパイラは次のメッセージを出力します。

    cc: Info: Feedback file prog does not exist (nofbfil)
    cc: Info: Compilation will proceed without feedback optimizations (nofbopt)
     
     
    

    [例に戻る]

  2. pixie コマンドは,計測機構付きのプログラム (prog.pixie) を作成して,それを実行します (prof オプション,-update が指定されているため)。 実行の統計情報とアドレスのマッピング・データは自動的に命令カウント・ファイル (prog.Counts) と命令アドレス・ファイル (prog.Addrs) に収集されます。 -update オプションにより,このプロファイル情報は拡張された実行可能ファイルに格納されます。 [例に戻る]

  3. -feedback オプションを指定した 2 度目のコンパイルでは,拡張された実行可能ファイル内のプロファイル情報がコンパイラと (-spike オプションを通じて) ポストリンク最適化プログラムを先導します。 このカスタマイズされたフィードバックは,-O3 および -spike オプションによる自動最適化よりも優れています。 コンパイラの最適化は,-ifo オプションや -assume whole_program オプションを -feedback オプションと組み合わせて使用すると,さらに効果が上がります。 ただし,10.1.1 項 で述べているように,大きいプログラムはソース・ファイルが 1 つしかない場合と同じようにコンパイルすることができないことがあります。 [例に戻る]

詳細は, pixie(1) および cc(1) を参照してください。

拡張された実行可能ファイルは,内部にプロファイル情報があるため,通常の実行可能ファイルよりも大きくなります (通常は 3 〜 5 パーセント)。 開発が完了したら,strip コマンドを用いてプロファイル情報とシンボル・テーブル情報を削除できます。 たとえば次のようにします。

% strip prog

spike コマンドは,strip を実行したイメージは処理できません。

例 4

一般的な開発過程では,Example 3 の手順 2 と 3 を必要な回数だけ繰り返して,ソース・コードの変更による影響が反映されるようにします。 たとえば次のようにします。

% cc -feedback prog -o prog -O3 *.c
% pixie -update prog
% cc -feedback prog -o prog -O3 *.c
[modify source code]
% cc -feedback prog -o prog -O3 *.c
.....
[modify source code]
% cc -feedback prog -o prog -O3 *.c
% pixie -update prog
% cc -feedback prog -o prog -spike -O3 *.c
 
 

拡張された実行可能ファイル内のプロファイル情報は,コンパイル操作では失われないので,情報を更新する pixie の処理手順は,ソース・モジュールが変更されて再コンパイルされるたびに繰り返す必要はありません。 ただし,変更のたびに,変更内容に応じて,実際のコードと古いフィードバック情報の違いが大きくなるため,最適化の有効度が低下します。 最後の変更および再コンパイルの後に pixie 処理手順を行うと,最後に行ったコンパイルに合わせてフィードバック情報が正確に更新された状態になります。

例 5

プロファイルの正確な情報を得るために,計測機構付きプログラムを異なる入力で複数回実行したい場合があります。 この例では,プログラム prog の実行を 2 回計測して,そのプロファイル統計情報をマージすることでプログラムを最適化する方法を示しています。 このプログラムの出力は,異なる入力で実行するたびに異なります。

% cc -feedback prog -o prog *.c [1]

% pixie -pids prog [2]

% prog.pixie [3]
(input set 1)
% prog.pixie
(input set 2)

% prof -pixie -update prog prog.Counts.* [4]

% spike prog -feedback prog -o prog.opt [5]

  1. 最初のコンパイルでは,Example 3 で説明したように拡張された実行可能ファイルが生成されます。 [例に戻る]

  2. 特に指定しなければ,計測機構付きプログラム (prog.pixie) を実行するたびに,prog.Counts という名前のプロファイル・データ・ファイルが作成されます。 -pids オプションを指定すると,計測機構付きプログラムを実行するたびに,作成されるプロファイル・データ・ファイルの名前にプロセス ID が付加されます (prog.Counts.pid)。 したがって,後の実行で生成されるデータ・ファイルで上書きされることはありません。 [例に戻る]

  3. 計測機構付きプログラムは 2 回実行され,そのたびに一意の名前でデータ・ファイルが生成されます。 たとえば,prog.Counts.371prog.Counts.422 のようになります。 [例に戻る]

  4. prof -pixie コマンドは,2 つのデータ・ファイルをマージします。 -update オプションにより,この結合された情報で実行可能ファイル prog が更新されます。 [例に戻る]

  5. -feedback オプション付きの spike コマンドは,最適化を先導するために 2 回のプログラム実行で得られたプロファイル情報を結合したものを使用し,最適化された出力ファイル prog.opt を生成します。 [例に戻る]

この例の最後の手順は,次のように変更できます。

% cc -spike -feedback prog -o prog -O3 *.c

-spike オプションでは,プログラムを再リンクする必要があります。 spike コマンドを使用した場合は,spike を実行するためにプログラムをリンクし直す必要はありません。

例 6

この例は,通常の (拡張されていない) 実行可能ファイルが作成され,spike コマンドの -fb オプションが使用される (-feedback オプションではない) 点で,Example 5 と異なります。

% cc prog -o prog *.c
% pixie -pids prog
% prog.pixie
(input set 1)
% prog.pixie
(input set 2)
% prof -pixie -merge prog.Counts prog prog.Addrs prog.Counts.*
% spike prog -fb prog -o prog.opt
 
 

prof -pixie -merge コマンドは,2 回の計測実行で得られた 2 つのデータ・ファイルを 1 つの prog.Counts ファイルにマージします。 この形式のフィードバックでは,-g1 オプションを明示的に指定して,プロファイリングに最適なシンボルを付ける必要があります。

spike -fb コマンドは prog.Addrs および prog.Counts の情報を使用して,最適化された出力ファイル prog.opt を生成します。

Example 5 の方法をお勧めします。 Example 6 の方法は互換性のためにサポートされているので,実行可能ファイルに格納されたフィードバック情報を使用する -feedback オプションを指定してコンパイルすることができない場合にのみ使用してください。

10.1.4    前処理と後処理に関する考慮事項

性能に影響を及ぼす前処理オプションと後処理 (実行時) オプションには,次のようなものがあります。

10.1.5    ライブラリ・ルーチンの選択

性能に影響を及ぼすライブラリ・ルーチンのオプションには,次のものがあります。

10.2    アプリケーションのコーディング上のガイドライン

アプリケーションを修正したい場合には,プロファイラ・ツールを使用して,アプリケーションの実行に最も時間がかかっている部分を判断します。 多くのアプリケーションでは,実行時間のほとんどは特定のルーチンで費やされています。 このような頻繁に使用されているルーチンについて,特に改善の努力を注ぐようにしてください。

Tru64 UNIX では,C および他の言語で作成されたプログラムに対して動作するいくつかのプロファイリング・ツールを提供しています。 詳細については,第 7 章第 8 章第 9 章prof_intro(1)gprof(1)hiprof(1)pixie(1)prof(1)third(1)uprofile(1) および atom(1)を参照してください。

アプリケーションで頻繁に使用されているルーチンが識別できたならば,そのコードが使用しているアルゴリズムについて検討してください。 より効率的なアルゴリズムと置換が可能なものはありませんか。 処理速度の遅いアルゴリズムは,既存のアルゴリズムに手を加えるより,処理速度の速いものと置換する方が,性能が大幅に改善されることがよくあります。

アルゴリズムの効率に問題がない場合は,コードを変更して,アプリケーションのオブジェクト・コードを生成するコンパイラが最適化を行いやすくすることを検討してください。 コンパイラによる最適化が最大限に行われるようなソース・コードの記述方法については,『High Performance Computing』(Kevin Dowd 著,O'Reilly & Associates, Inc., ISBN 1-56592-032-5) に,一般的な説明があります。

以降の各項で,性能改善について考慮の対象となるデータ型,入出力処理,キャッシュ使用とデータの境界合わせ,および言語固有の問題について説明します。

10.2.1    データ型についての考慮事項

性能に影響を及ぼすデータ型に関する考慮事項には,次のものがあります。

10.2.2    AdvFS ファイルでの直接入出力の使用

直接入出力を使用すると,ファイル管理,オンライン・バックアップ,およびオンライン・リカバリなどの AdvFS (Advanced File System) が提供するファイル・システム機能を利用できるようになります。 しかも,ユーザ・データを AdvFS キャッシュにコピーすることによるオーバヘッドはありません。 直接入出力では,直接メモリ・アクセス (DMA) コマンドを使用して,アプリケーションのバッファとディスク間でユーザ・データを直接コピーします。

通常のファイル・システム入出力では,ファイル・ページをキャッシュ内に保持します。 これにより,入出力が非同期に完了します。 いったんデータがキャッシュ内に蓄えられて,入出力のスケジュールが設定されると,アプリケーションはデータがディスクに転送されるのを待つ必要はありません。 さらに,データが既にキャッシュ内にあるため,その後このページにアクセスする際には,ディスクからデータを読み込む必要はありません。 ほとのどのアプリケーションは通常のファイル・システム入出力を使用します。

通常のファイル・システム入出力は,ディスク上のデータに稀にしかアクセスせずに,スレッド間の競合を管理するアプリケーションには適しません。 この種のアプリケーションでは,直接入出力による低いオーバヘッドの利点を利用できます。 ただし,データはキャッシュされないため,指定されたページへのアクセスを,競合するスレッド間で直列化する必要があります。 これを行うため,省略時の設定では,直接入出力は同期入出力を実行します。 これは,read() ルーチンがアプリケーションに制御を戻す時点で,入出力が完了しており,データはディスク上にあることを意味します。 その後,そのデータを取得する場合は,常にディスクからデータを取得するための入出力操作が行われます。

アプリケーションは,非同期入出力 (AIO) を利用することもできますが,その場合でも aio_read() および aio_write() システム・ルーチンを使用することにより,基礎となる直接入出力のメカニズムを使用しています。 これらのルーチンは,データがディスクに転送される前にアプリケーションに制御を戻します。 また aio_error() ルーチンは,アプリケーションが入出力の完了に対してポーリングを行うことを可能にします (カーネルは,ファイル・ページへのアクセスの同期処理を行って,2 つのスレッドが同時に同じページへ書き込みを行わないようにします)。

直接入出力を使用して指定されたファイルへアクセスする複数のスレッドは,同じページ範囲にアクセスしなければ,その作業を同時に行うことができます。 たとえば,スレッド A がページ 10 から 19 までに書き込みを行い,スレッド B がページ 20 から 39 までに書き込みを行う場合,これらの操作は同時に実行されます。 これに続いて,スレッド B がページ 15 から 39 まで単一の直接入出力転送で書き込みを行う場合には,ページ範囲が重複しているため,スレッド A の書き込みが完了するまでスレッド B は待機させられます。

直接入出力を使用するときには,要求された転送がディスク・セクタ境界に合っていて,転送サイズが基本セクタ・サイズの偶数倍である場合に,最高の性能が得られます。 最適な転送サイズはデータ格納用のハードウェアに依存しますが,一般に転送サイズが大きいほど,転送効率は向上します。

注意

直接入出力モードとマップされたファイル領域 (mmap) の使用は,排他的な操作です。 マップされたファイル領域を使用するファイルに対して,直接入出力モードを設定することはできません。 直接入出力用に既にオープンされているファイルに対してマッピングを行う場合も,操作は失敗します。

直接入出力およびアトミック・データ・ロギング・モードも,相互に排他的です。 ファイルがこれらのいずれかのモードでオープンされている場合に,同じファイルを他のモードでオープンしようとすると,操作は失敗します。

AIO アプリケーションおよび非 AIO アプリケーションの両方について,直接入出力機能を有効にして,AdvFS ファイルで使用できます。 この機能を利用できるようにするには,O_DIRECTIO ファイル・アクセス・フラグを設定し,アプリケーションで open 関数を使用してます。 次に例を示します。

 open ("file", O_DIRECTIO | O_RDWR, 0644)

直接入出力モードは,すべてのユーザによりファイルがクローズされるまで有効です。

fcntl() 関数にパラメータ F_GETCACHEPOLICY を指定して使用すると,ファイルのキャッシング・ポリシ (FCACHE または FDIRECTIO モード) を返すことができます。 次に例を示します。

int fcntlarg = 0;
ret = fcntl( filedescriptor, F_GETCACHEPOLICY, &fcntlarg );
if ( ret != -1 && fcntlarg == FDIRECTIO ) {
.
.
.
 

直接入出力および AdvFS の使用法についての詳細は, fcntl(2) および open(2) を参照してください。

10.2.3    キャッシュ使用とデータの境界合わせに関する考慮事項

キャッシュ使用パターンは,次のように,性能にクリティカルな影響を及ぼします。

データの境界合わせも性能に影響を及ぼします。 省略時の設定では,C コンパイラは,各データ項目を自然境界に合わせます。 つまり,各データ項目を,その開始アドレスが,宣言に使用されたデータ型のサイズの倍数になるように位置付けます。 自然境界に位置合わせされていないデータを境界合わせの間違ったデータと呼びます。 境界合わせの間違ったデータは,実行時に,ソフトウェアで必要な調整を行うため,性能が落ちることがあります。

C プログラムでは,ポインタ変数を,あるデータ型からサイズの大きなデータ型へ型キャストした場合に,境界合わせ間違いが起こることがあります。 たとえば,char ポインタ (1 バイトの境界合わせ) を int ポインタ (4 バイトの境界合わせ) へ型キャストした後,新しいポインタを逆参照すると,境界合わせされていないアクセスが発生することがあります。 また,C では,#pragma  pack 指示文を使用してパック構造体を作成しても,境界合わせされていないアクセスが行われることがあります。 #pragma  pack 指示文についての詳細は,第 3 章を参照してください。

C プログラムでの境界合わせの問題を修正するには,-misalign オプション (または -assume noaligned_objects) を使用するか,ソース・コードに必要な変更を行います。 何らかの理由によって,ユーザ・プログラムで境界合わせ間違いのインスタンスを必要とする場合には,境界合わせの間違ったデータを呼び出すすべてのポインタ宣言で _ _unaligned データ型識別子を使用します。 _ _unaligned として宣言されたポインタを使用してデータがアクセスされると,コンパイラは,境界合わせエラーを発生させずにデータのコピーや格納を行うために必要な追加コードを生成します。 境界合わせエラーは,性能に対して,生成される追加コードよりもはるかに重大な影響を与えます。

C プログラムのコンパイル中には,境界合わせの間違ったデータを示す警告メッセージは表示されません。 ただし,プログラムの実行中には,境界合わせが間違っているデータがあると,カーネルが警告メッセージ ("unaligned access") を表示します。 メッセージには,境界合わせ間違いを引き起こした命令のアドレスを示すプログラム・カウンタ (PC) 値が示されます。

次のいずれかの方法を使用して,unaligned access の問題を引き起こすコードにアクセスすることができます。

データの境界合わせについての詳細は,『Alpha Architecture Reference Manual』を参照してください。 コンパイル・コマンド行に指定できる境界合わせを制御するオプションについての詳細は, cc(1) を参照してください。

10.2.4    一般的なコーディングに関する考慮事項

一般的なコーディング上の考慮事項には,次のものがあります。

また,逆参照の結果を格納する場合にはローカル変数を使用し,できるだけ別名を使用しないようにしてください。 逆参照の結果とは,指定されたアドレスから得られる値のことです。 逆参照の結果は,間接参照の演算および呼び出しによって影響を受けますが,ローカル変数は影響を受けません。 これは,ローカル変数は,レジスタに保持できるためです。 例 10-1 は,ポインタを適切に配置し,別名を削除することによって,コンパイラがより良いコードを生成することを示したものです。

例 10-1:  ポインタと最適化

int len = 10;
char a[10];
 
void
zero()
  {
  char *p;
  for (p = a; p != a + len; ) *p++ = 0;
  }

例 10-1 のポインタの使用方法について考慮してみてください。 文 *p++ = 0len を変更することがあるので,コンパイラはループの外側でレジスタを使用して a  +  len を 1 度だけ演算するのではなく,ループを 1 回実行するたびにメモリから len をロードして,a のアドレスに加算しなければなりません。

次の 2 つの方法を使用すると,例 10-1 のコードの効率を改善することができます。