【PL/I深層解説】FIXED BINARYの「境界整列」が運命を分ける―CPU効率とメモリの最適化
現場の諸君、今日もメインフレームの迷宮でバグと格闘していることだろう。
最近、レガシーシステムのマイグレーションや性能改善の相談を受ける際、データ定義の「なんとなく」が原因で、バッチ処理のCPU時間が想定以上に膨れ上がっているケースをよく目にする。特に`FIXED BINARY(15)`と`(31)`の使い分け、君たちは正しく理解できているか?
単に「値の範囲が小さいから15でいいや」と考えているなら、それは危険なサインだ。今日は、IBMメインフレームのCPUアーキテクチャが泣いて喜ぶ、PL/Iのメモリ境界整列(アライメント)の話をしよう。
—
1. なぜ「境界整列(Alignment)」が重要なのか
IBMのz/Architectureにおいて、CPUはメモリ上のデータを読み込む際、そのデータが「自身のサイズに応じた境界」に配置されていることを強く期待している。
- FIXED BINARY(15): 2バイト(ハーフワード)境界
- FIXED BINARY(31): 4バイト(フルワード)境界
もし、`(31)`で定義したデータが奇数番地など、4バイト境界に整列されていない場所に配置されていると、CPUは「メモリアクセスの境界違反」を補正するために、内部で余計なロード命令や再整列処理を走らせる。これが積み重なると、数十万件のレコードを処理するバッチでは、目に見えるほどの性能劣化(オーバーヘッド)を引き起こすんだ。
2. 実践:データ構造と内部表現
以下のコードを見てほしい。構造体の中でこれらをどう定義するかで、コンパイラが生成するメモリマップは劇的に変わる。
1
/ ————————————————————- /
/ 構造体の境界整列を意識したデータ定義例 /
/ ————————————————————- /
DCL 1 WK_RECORD,
5 REC_TYPE FIXED BIN(15), / 2バイト: ハーフワード境界 /
5 FILLER_1 CHAR(2), / 境界整列のためのパディング /
5 REC_VALUE_31 FIXED BIN(31), / 4バイト: フルワード境界に配置 /
5 REC_STATUS FIXED BIN(15); / 2バイト /
/
解説:
FILLERを挟むことで、REC_VALUE_31を確実にフルワード境界に
配置させている。アライメントを意識しないと、コンパイラは
自動調整を行うが、予期せぬパディングが発生してメモリが無駄に
なることもある。
/
3. コンパイラオプションと最適化の罠
PL/Iコンパイラには、`ALIGN`と`UNALIGNED`という属性がある。デフォルトの`ALIGN`は、境界整列を優先して高速なロード命令を生成する。一方で`UNALIGNED`を指定すると、パディングを極限まで省き、メモリ消費を抑えることができる。
しかし、現代のメインフレームにおいて「メモリ節約」のために`UNALIGNED`を乱用するのは愚策だ。CPUの演算効率こそが、バッチウィンドウを死守するための最大の鍵だからだ。
VSAMアクセス時の注意点
VSAMファイルへのアクセスにおいて、レコードレイアウトを定義する際は、必ず`ALIGNED`属性を明示的に、あるいはデフォルトの挙動を理解してレイアウトを組むようにしてほしい。そうでないと、読み込んだ瞬間にアライメント違反のペナルティが加算され続けることになる。
4. ONユニットと例外ハンドリングの作法
データが境界整列されていない状態で、誤ったポインタ演算や型変換を行うと、`CONVERSION`や`FIXEDOVERFLOW`といった例外が発生する。現場ではこれらをONユニットで拾うことが定石だが、「例外を拾うこと」を前提にコードを書くのは避けろ。 境界整列を正しく行うだけで、これらの例外はそもそも発生しなくなるはずだ。
1
/ ONユニットの正しい使い道は、予期せぬ事態のログ出力だ /
ON CONVERSION BEGIN;
PUT SKIP LIST(‘データ変換エラー発生: 計算ロジックを見直せ’);
SIGNAL ERROR; / 異常終了させるのが基本 /
END;
/ ビルトイン関数の活用例 /
/ BINARY(X, 31)を使用して、明示的に型を合わせる /
DCL WORK_VAL FIXED BIN(31);
WORK_VAL = BINARY(INPUT_VAL, 31);
5. ベテランからのアドバイス
後輩によく言うのは、「コンピュータに、データの読み込み場所を迷わせるな」ということだ。
1. 基本はFIXED BIN(31): 現代のメインフレームにおいて、(15)と(31)でメモリ消費量の差は微々たるものだ。演算速度の安定性を重視し、特に計算に使う変数は迷わず(31)を使え。
2. 構造体のパディングを可視化せよ: コンパイラリストの「Storage Map」を必ず確認すること。どこにパディング(無駄なバイト)が入っているかを知ることは、アーキテクトとしての第一歩だ。
3. プロファイリングを怠るな: 「なんとなく遅い」で済ませるな。どこでCPUサイクルを食っているか、ツールを使ってボトルネックを特定する。
PL/Iは、古い言語に見えて、z/Architectureの性能を極限まで引き出せる強力なツールだ。この「境界整列」という基本をマスターするだけで、君の書くコードの「重厚感」は段違いになるはずだ。
さて、今日はここまで。次回の改修案件でも、この「アライメント」を意識した美しい設計を見せてくれ。期待している。
