こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいはビジネスチルドレンな言語をバリバリ書いてきた方にとって、PL/I(Programming Language One)という名前を聞くだけで、なんだか古めかしくて難解な要塞のように感じてしまうかもしれませんよね。
「変数の命名規則は?」「構造体ってどうなってるの?」と、最初は戸惑うこともあるでしょう。でも、安心してください。一つひとつの仕様を紐解いていけば、PL/Iは非常に合理的で、マシンパワーを極限まで引き出すための「美しいこだわり」に満ちた言語なんです。
今回は、そんなPL/Iのデータ制御において避けて通れない、「構造体アライメント(ALIGNEDとUNALIGNED属性)」の世界へご案内します。メモリ効率と処理速度のせめぎ合い、そしてメインフレームのハードウェアの仕組みにちょっとだけ触れながら、優しく丁寧に解説していきますね。
—
1. 他言語からの挑戦者たちが最初に驚く「メモリの配置」の話
JavaやCOBOLで構造体(COBOLなら01レベルの集団項目)を定義するとき、メモリ上でそのデータがどう並んでいるか、あまり意識したことはないかもしれません。コンパイラやランタイムがよしなにやってくれるからです。
しかし、PL/Iの世界では、データがメモリ上で「どう座っているか(レイアウト)」をプログラマが明示的、あるいは暗黙的にコントロールする場面に出くわします。それが ALIGNED(アライメント) と UNALIGNED(アンアライメント) という属性です。
まずは、イメージしやすいように例え話をさせてください。
> 【本棚の例え】
> あなたが大量の本(データ)を本棚(メモリ)に整理して収納するとします。
> ALIGNED(CPU最優先):本を種類ごとに、必ず「棚のキリの良い位置(2の倍数番地や4の倍数番地など)」から並べます。本の間に少し隙間(デッドスペース)ができても気にしません。とにかく取り出すときにサッと手が届く(CPUが高速にアクセスできる)状態にします。
> UNALIGNED(メモリ容量最優先):本の間に一切の隙間を作らず、ピタピタ詰めて並べます。隙間がないのでメモリの節約になりますが、中途半端な位置にある本を取り出すとき、コンパスで測るようにちょっと手間取ります(CPUが複数回に分けて読み込む必要がある場合がある)。
PL/Iの構造体では、この「本棚の並べ方」を私たちが指定できるのです。
—
2. 実際のPL/Iコードで違いを見てみよう
百聞は一見に如かず。実際にPL/Iで構造体を定義してみましょう。
レガシーな現場の雰囲気を味わうために、コードは大文字で記述します。
1
/ 構造体アライメントの比較サンプル /
DCL 1 EMPLOYEE_ALIGNED ALIGNED,
2 EMP_ID FIXED BIN(31), / 4バイトの整数 /
2 EMP_NAME CHAR(5), / 5バイトの文字 /
2 EMP_AGE FIXED BIN(15); / 2バイトの整数 /
Dcl 1 EMPLOYEE_UNALIGNED UNALIGNED,
2 EMP_ID FIXED BIN(31), / 4バイトの整数 /
2 EMP_NAME CHAR(5), / 5バイトの文字 /
2 EMP_AGE FIXED BIN(15); / 2バイトの整数 /
一見すると、どちらも同じフィールドを持っているように見えますよね。
しかし、メモリ上での占有サイズと配置はまったく異なります。
ALIGNED(デフォルト、または明示指定)の世界
メインフレームのCPU(IBM Zアーキテクチャ)は、4バイトのデータは4の倍数のアドレス(境界)から、2バイトのデータは2の倍数のアドレスから読み書きするのを好みます(境界整列)。
`EMPLOYEE_ALIGNED` の場合:
1. `EMP_ID` (4バイト) は 4の倍数の位置に置かれます。
2. 次の `EMP_NAME` (5バイト) が来ますが、その次の `EMP_AGE` (2バイト:2の倍数境界が必要) をキレイに収めるために、コンパイラが間に 1バイトのパディング(隙間・ダミー領域) を勝手に挟み込みます。
3. 結果として、構造体全体のサイズは「4 + 5 + (パディング1) + 2 = 12バイト」になります。
UNALIGNED(メモリ節約)の世界
一方、`UNALIGNED` を指定すると、CPUの好みよりも「メモリの節約」が優先されます。
`EMPLOYEE_UNALIGNED` の場合:
1. パディングは一切入りません。
2. `EMP_ID` (4バイト) のすぐ後ろに `EMP_NAME` (5バイト) がピタッとくっつき、その直後に `EMP_AGE` (2バイト) が配置されます。
3. 結果として、構造体全体のサイズは「4 + 5 + 2 = 11バイト」になります。
たった1バイトの差だと思うかもしれませんが、これが何百万件も処理される巨大なマスターファイルや、通信用のバッファ(外部インターフェース)だったらどうでしょう? 積もり積もって大きなメモリ圧迫や、外部システムとのデータレイアウト不一致(いわゆる「電文ズレ」)の原因になってしまいます。
—
3. どっちを使うべき? 現場で迷ったときの指針
「じゃあ、全部UNALIGNEDにすればメモリ節約になってお得じゃん!」と思ったそこのあなた、ちょっと待ってください。世の中そんなに甘くはありません。
メインフレームのアーキテクチャにおいて、境界整列されていない(UNALIGNEDな)データをCPUが無理やり読み書きすると、内部的に余計なマシン命令が発生し、パフォーマンスが低下するというペナルティがあります。近年の z/Architecture は非常に優秀なので昔ほどシビアではありませんが、超高スループットを求められる基幹系バッチでは無視できない要素です。
実務の現場では、次のような基準で使い分けるのが鉄則です。
- UNALIGNEDを使うべきケース
- 他のシステム(C言語やCOBOL、あるいは外部のファイル・通信電文)とレイアウトを1バイト単位で完全に一致させる必要があるとき。
- メモリやディスクの容量を極限まで削りたい巨大なテーブル定義のとき。
- (※PL/Iの文字型 `CHAR` やビット型 `BIT` は、デフォルトでUNALIGNEDであることが多いため、文字主体のデータ構造では自然とこちらになりがちです)
- ALIGNEDを使うべきケース
- 外部インターフェースに関係なく、プログラム内で完結するワークエリア(作業用領域)で、数値計算をバリバリ行うとき。
- CPUのアクセス効率を最大化させたいとき。
—
4. まとめ:怖がらなくて大丈夫、PL/Iはあなたの味方です
今回は、PL/Iの構造体における `ALIGNED` と `UNALIGNED` の違いについて紐解いてみました。
- ALIGNED は「CPUの速度重視! キリの良い場所に配置するよ(パディングが入ることもある)」
- UNALIGNED は「メモリ容量・レイアウトの一致重視! 隙間なくピタッと詰めるよ」
最初は呪文のように思えるPL/Iの属性も、「ハードウェアと仲良くするための工夫なんだな」と分かれば、ぐっと親しみやすく感じられるはずです。レガシー移行や保守の現場でこれらの記述に出会っても、もう深呼吸して落ち着いて読み解けますよね。
古いシステムには、その時代を生き抜いてきた先人たちの知恵と工夫がぎっしり詰まっています。一つひとつ紐解いていけば、決して怖くありません。
あなたのメインフレーム学習の旅を、これからも応援しています!
