こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネス標準の言語をバリバリ書いてきた方にとって、IBMメインフレームの「PL/I(ピーエルワン)」という名前を聞くだけで、なんだか古めかしい黒い画面と難しい呪文のようなコードを想像して身構えてしまうかもしれません。
「データ定義ってどうやるの?」
「なんか変な略語がたくさん出てくるんだけど……」
大丈夫です、安心してください。怖がる必要は全くありません。
今回は、PL/Iの基本データ型の中でも、金融や基幹システムの心臓部である「固定小数点数(FIXED BINARY / DECIMAL)」と、それを支えるコンパイラの最適化(`OPTIMIZE`オプション)について、ふんわりと、でも核心をつく形で優しく紐解いていきたいと思います。
Javaの `int` や COBOLの `COMP-3` とはどう違うのか、そしてメインフレームのCPUが裏でどんな汗をかいて計算しているのか、のぞいてみましょう!
—
1. そもそもPL/Iの「固定小数点数」ってなに?
Javaなら `int` や `long`、COBOLなら `PIC S9(9) COMP` などでお馴染みの数値データですね。PL/Iでも、数値をきちっと管理するために固定小数点数が用意されています。
大別して以下の2つの書き方があります。
1. `FIXED DECIMAL`(十進固定小数点数):人間が普段使う10進数ベース。COBOLのパック十進数(COMP-3)のイメージに近いです。
2. `FIXED BINARY`(二進固定小数点数):コンピュータが大好きな2進数ベース。Javaの整数型やCOBOLの二進項目(COMP)に近いですね。
ここで、PL/Iのちょっとユニークな(そして初学者が戸惑う)宣言の仕方をみてみましょう。
DCL WK_DEC_AMT FIXED DEC(9,2); / 全体で9桁、そのうち小数点以下が2桁の十進数 /
DCL WK_BIN_CNT FIXED BIN(31); / 32ビット(符号付き)の二進数カウンタ /
「あれ? `FIXED BIN(31)` なのになんで 31 なの? 32じゃないの?」と思いませんでしたか?
これ、すごくよくある疑問なんです。PL/Iの `FIXED BIN` の括弧の中は、「有効桁数(ビット数)」を表します。符号(プラスかマイナスか)の1ビットを除いた、純粋に数字を表すためのビット数を指定するお約束になっているんです。だから `31` と書くと、ちょうどJavaの `int` と同じ、32ビット(符号1 + データ31)の立派な整数ボックスができあがります。
—
2. なぜ演算の裏側を知る必要があるのか?(CVBとCVDの物語)
さて、ここからが今回のメインテーマです。
JavaやCOBOLでは、プログラマが「CPUがどうやって計算しているか」を意識することは普段ほとんどありませんよね。`A = B + C` と書けば勝手に計算してくれます。
しかし、IBMメインフレーム( z/Architecture )の世界では、私たちが書いたPL/Iのデータ型の組み合わせによって、コンパイラが吐き出す機械語の命令がガラリと変わります。特に、十進数(`FIXED DEC`)と二進数(`FIXED BIN`)が混ざった計算をさせるときがドラマの始まりです。
- `CVB` (Convert to Binary):十進数のデータを、CPUが計算しやすい二進数に「よいしょっ」と変換する命令。
- `CVD` (Convert to Decimal):計算が終わった二進数を、人間や帳票が見慣れた十進数に「お化粧直し」して戻す命令。
もし、プログラムの中で `FIXED DEC` と `FIXED BIN` の足し算や代入が頻繁に発生していると、コンパイラは計算のたびに裏でこの `CVB` や `CVD` をこっそり実行します。
実は、この「データ型の変換(コンバート)」、CPUにとっては結構な重労働なんです。
—
3. `OPTIMIZE` オプションがもたらす魔法
ここで登場するのが、PL/Iコンパイラの誇る強力な助っ人、`OPTIMIZE`(最適化)オプションです。
コンパイル時に `OPTIMIZE(TIME)` や `OPTIMIZE(SPACE)` を指定すると、コンパイラはソースコードを隅々まで見渡し、「おっ、この変数、ループの中で何度も十進数から二進数に直してるな……めんどくさいから、最初から二進数に固定しちゃおう!」といったような、賢いコードの書き換え(最適化)を行ってくれます。
百聞は一見にしかず。簡単なサンプルコードでその雰囲気を感じてみてください。
/ ======================================================== /
/ 固定小数点演算と最適化のイメージを掴むためのPL/Iサンプル /
/ ======================================================== /
TEST_OPT: PROC OPTIONS(MAIN);
/ 宣言部:十進数と二進数の出会い /
DCL 1 W_DATA,
3 W_DEC_VAL FIXED DEC(9,0) INIT(0), / 十進数のワーク /
3 W_BIN_VAL FIXED BIN(31) INIT(0); / 二進数のカウンタ /
Dcl I FIXED BIN(15);
/ ループ処理:ここで型変換が頻発する可能性があります /
DO I = 1 TO 1000;
/ FIXED DEC と FIXED BIN の混在演算 /
W_DEC_VAL = W_DEC_VAL + 1;
W_BIN_VAL = W_BIN_VAL + I;
END;
PUT SKIP LIST(‘計算完了!’);
END TEST_OPT;
このコード、一見なんの変哲もないループですが、`OPTIMIZE` オプションなしでコンパイルすると、コンパイラは「指示された通りに、忠実に、毎回律儀に型変換を行おう」とします。その結果、生成されるアセンブラコード(機械語)には `CVB` や `CVD` がオンパレードになり、バッチ処理全体の実行時間がチリも積もれば山となで、じわじわと遅くなってしまうのです。
しかし、`OPTIMIZE(TIME)` を有効にしてコンパイルすると、コンパイラは次のように気を利かせてくれます。
1. ループ内で無駄に行われている型変換のパターンを見抜く。
2. レジスタ(CPU内の超高速な作業机)の上だけで計算を完結させ、メモリとCPU間の往復を最小限にする。
3. 結果として、重たい `CVB`/`CVD` の呼び出し回数を劇的に減らす。
—
4. レガシー移行・実務現場でのアドバイス
JavaやCOBOLからの移行プロジェクト、あるいは既存のPL/Iバッチプログラムのチューニング現場に立ち会うと、こんな場面によく遭遇します。
- 「なんかこのプログラム、データ件数が増えると急に遅くなるぞ?」
- 「仕様変更で、テキトウに `FIXED DEC` と `FIXED BIN` を混ぜて足し算しちゃったけど大丈夫かな……」
怖がらなくて大丈夫です!
基本の指針として、以下の2つを心掛けるだけで、コンパイラの最適化機能は最大限に力を発揮してくれます。
1. 算術演算を行う変数の型はできるだけ統一する
できればループ内や頻繁に計算する箇所では、CPUの得意な `FIXED BINARY`(特に `FIXED BIN(31)` など)で統一してあげると、コンパイラが余計な変換コードを挟む必要がなくなります。
2. コンパイルオプションを見直す
プロダクション環境(本番環境)のビルド JCL では、必ず `OPTIMIZE(TIME)` (またはコンパイラに応じた適切な最適化レベル)が指定されているか確認しましょう。これだけで、CPU使用率(CP秒)がごっそり削れることも珍しくありません。
—
お疲れ様でした!
PL/Iの `FIXED BIN` や `FIXED DEC`、そしてコンパイラの最適化と聞くと難しく感じたかもしれませんが、要するに「コンピュータが一番計算しやすい形(二進数)に、いかにスムーズに仕事をさせてあげるか」という、人間味あふれるチューニングの話なのです。
レガシーシステムの裏側では、こうしたコンパイラの優しさと工夫が今この瞬間も動いています。
一つひとつのデータ型とオプションの意味が分かってくると、メインフレームを触るのがぐっと楽しくなりますよ。ぜひ日々の開発や調査の参考にしてみてくださいね!
