【テクニカル・上級編】FIXED BINARY(15)と(31)の演算パフォーマンス差異 – PL/Iの基本構文とデータ制御実践ガイド

メインフレームの深淵:FIXED BINARY(15) vs (31) が分かつCPUパフォーマンスと移行の罠

メインフレームの現場で長くPL/Iを触っていると、「たかが型定義」と侮った瞬間、性能解析の泥沼に足を取られることになる。特に`FIXED BINARY(15)`と`(31)`。この僅かな宣言の差が、z/Architectureにおける命令セットの選択を変え、ひいてはCICSのレスポンスやバッチのCPU使用時間に直結する。

今日は、JavaやC#へのマイグレーションを控えたアーキテクト諸氏に向けて、PL/Iのデータ制御という「低レイヤーの真実」を共有しよう。

1. ハーフワード(15)とフルワード(31)の命令セットの壁

PL/Iにおいて`FIXED BINARY(15)`は、内部的には2バイトのハーフワードとして扱われる。一方、`(31)`は4バイトのフルワードだ。

現代のz/Architectureにおいて、CPUは32ビット演算(フルワード)を基本単位として最適化されている。ここで重要なのは、コンパイラが生成する機械語の挙動だ。

  • FIXED BINARY(15): ハーフワード演算を行う際、CPUはロード時に符号拡張(Sign Extension)を行い、演算後に再びハーフワードへ切り詰める処理を伴うことが多い。コードサイズは小さくなるが、レジスタ間のデータ移動時にサイクルを浪費する可能性がある。
  • FIXED BINARY(31): ネイティブの32ビットレジスタ演算と完全に一致する。現代のパイプラインではこちらの方が圧倒的に効率的だ。

実践的なコード例:意識すべき宣言と演算

1
/ 効率を求めるなら、インデックスやカウンタは(31)を推奨する /
DCL I FIXED BIN(15) INIT(0); / ループカウンタ等で多用されがち /
DCL J FIXED BIN(31) INIT(0); / CPUレジスタと相性が良い /

/ 演算時に(15)と(31)が混在すると、暗黙の型変換(Promotion)が発生する /
/ これがコンパイラの意図しない中間コード生成を誘発する /
J = J + I; / コンパイラはIを一時的に31ビット相当に拡張してから加算する /

2. オーバーフローの「静かなる死」とダンプ解析

PL/Iの恐ろしい点は、デフォルトで例外処理が抑制されている場合があることだ。`FIXED BINARY(15)`の上限値は32,767。これを超えた瞬間に発生するオーバーフローは、しばしば「データの化け」として後続のロジックを汚染する。

特に、Javaへの移行を計画しているなら注意が必要だ。Javaはオーバーフロー時に例外を投げないケース(`int`のラップアラウンド)もあれば、意図せぬ挙動を生むこともある。

ダンプ解析の鉄則:
アベンド(S0C7やS0C1)が発生した際、レジスタを確認し、期待値より大きな値が保持されていないかを追え。PL/Iは変数の境界を厳格にチェックしないため、ポインタを使ってベース変数を操作している場合、隣接するデータ領域を破壊している可能性が高い。

3. パックデシマルとSQLの落とし穴

メインフレームの基幹システムでは、DB2の`DECIMAL`型とPL/Iの`FIXED DECIMAL`型を多用する。ここで最も厄介なのが、内部符号の反転バグだ。

DB2からフェッチした際に、末尾のニブル(符号部)が正しく解釈されないと、計算結果が正負逆転する。特にホスト言語とDB間のマイグレーションにおいて、バイナリ転送時にこの符号部が破壊されることは、マイグレーションにおける「最も高コストな手戻り」の原因となる。

1
/ DB2から読み込むデータの定義例 /
DCL 1 HOST_VAR_RECORD,
5 AMOUNT FIXED DEC(15, 2), / パックデシマル /
5 STATUS_CODE CHAR(1);

/ 埋め込みSQL: データ整合性を担保する /
EXEC SQL FETCH C1 INTO :HOST_VAR_RECORD.AMOUNT, :HOST_VAR_RECORD.STATUS_CODE;
/ ここでダンプを仕込み、符号ビットが正しいかを確認する習慣を付けよ /

4. アーキテクトへの提言:移行を見据えた設計

今後、COBOLやPL/IからJava/C#へ移行する際、最も障壁となるのは「型の曖昧さ」だ。

1. 最適化オプションの再考: コンパイラオプション`OPTIMIZE(3)`を過信せず、演算のボトルネックはアセンブラレベルのリスト(`LIST`オプション)を見て判断する癖をつけること。
2. メモリレイアウトの固定: ポインタ操作(`ADDR`関数や`OFFSET`)に依存したコードは、マイグレーション時に地獄を見る。現在主流のオブジェクト指向言語にない「自由すぎるメモリレイアウト」は、早急に構造化データ型へのリファクタリングを推奨する。
3. 境界チェックの徹底: `CHECK`コンパイラオプションは開発環境では必須だ。本番環境でこれを外すのは、目隠しをして高速道路を走るようなものだ。

PL/Iは、CPUを掌中で操るための強力な道具だ。しかし、その力は「なぜその型を選ぶのか」「なぜその命令が生成されるのか」を理解した者にしか与えられない。レガシーシステムの裏側にある、この美しくも残酷な理屈を忘れないでほしい。それが、確実な移行を成功させる唯一の道だ。

タイトルとURLをコピーしました