こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといったモダン、あるいは従来型のビジネス言語をバリバリ書いてこられた方にとって、PL/I(ピーエルアイ)という名前を聞くだけで「なんだか古めかしくて難しそう…」「記号が多くて怖い…」と身構えてしまうかもしれませんよね。
でも、どうぞ安心してください。どんなにベテランのアーキテクトであっても、最初はみんな「なんだこれ?」という戸惑いからスタートしています。一つひとつの仕様を紐解いていけば、PL/Iがいかに合理的に作られた言語であるかが分かってきます。
さて、今回はPL/Iのデータ制御における隠れた重要テーマ、「CHARACTER属性の内部表現とアライメント(境界調整)」について、じっくりとお話ししていきますね。
メモリの配置やパフォーマンスの裏側なんて聞くと難しそうに思えるかもしれませんが、普段私たちが暮らす「お部屋の収納」に例えながら優しく解説していきますので、コーヒーでも飲みながらリラックスして読んでいってください。
—
1. 他言語からの挑戦者たちが驚く、PL/Iの「文字」と「メモリ」の世界
JavaやCOBOLで文字列を扱うとき、私たちはあまりメモリの物理的な配置を意識しませんよね。COBOLであれば `PIC X(10)` と書けば10文字分の領域が確保され、Javaなら `String` 型がよしなに裏側を管理してくれます。
では、PL/Iで固定長文字列を宣言する代表的な書き方を見てみましょう。
1
DCL EMP_NAME CHAR(10); / 10バイトの固定長文字列 /
これ自体はとてもシンプルです。「従業員名を格納する10文字(10バイト)の箱だな」と直感的に分かりますよね。
しかし、メインフレーム(z/Architecture)の世界では、CPUがメモリを読み書きする際に「効率の良いアドレス(番地)」というものが存在します。これが、今回のテーマであるアライメント(境界調整)です。
—
2. メモリの「キリのいい場所」ってなに?(お部屋の収納に例えてみよう)
メインフレームのCPUは、メモリからデータをロードする際、奇数番地からモリモリ読み込むよりも、2の倍数、4の倍数、あるいは8の倍数といった「キリのいい番地(境界)」からアクセスする方が、圧倒的に機嫌よく(=高速に)動くように設計されています。
これを分かりやすく、「本棚の本の並べ方」に例えてみましょう。
- 境界調整(アライメント)が考慮されていない世界:
厚さのバラバラな本を、隙間なく無理やりギュウギュウに詰め込んだ状態です。本を取り出すときに、隣の本まで引っ張り出してしまったりして、ちょっと余計な手間(CPUの追加サイクル)がかかりますよね。
- 境界調整(ALIGNED属性)の世界:
分厚い本は必ず「棚の区切りのいい位置」から並べ始めるルールにした状態です。パッと手を伸ばせば、一発でお目当ての本を取り出せます。
PL/Iでは、変数を宣言するときに、この「棚のどこから置き始めるか」をコンパイラに指示(あるいは自動制御)することができます。それが ALIGNED(境界合わせをする) と UNALIGNED(詰め込んで配置する) という属性です。
—
3. CHARACTER属性における「奇妙なデフォルト」の正体
ここで、PL/Iのちょっとユニークで、初心者が一番最初にハマりやすいポイントをご紹介します。
実は、PL/Iの `CHARACTER`(固定長文字列)変数をデフォルトのまま宣言すると、コンパイラはそれを UNALIGNED(非境界調整=隙間なく詰める) として扱います。
えっ、さっき「キリのいい場所(ALIGNED)の方がCPUに優しい」って言いませんでしたか?なぜ文字型はデフォルトで詰めて配置されるのでしょうか?
理由はシンプルです。「文字データは1バイト単位でシフトや演算をすることが多く、メモリの無駄(パディングによる隙間)を嫌うから」です。
例えば、1文字(1バイト)の `CHAR(1)` をアライメントの都合で4バイトごとに配置していたら、メモリがスカスカになってしまいますよね。それを防ぐために、文字列の基本は「ギュッと詰める(UNALIGNED)」がデフォルトになっています。
しかし、これが構造体(STRUCTURE)の中に組み合わされてくると、話が変わってきます。
—
4. 実務で遭遇する構造体とアライメントの罠
レガシーシステムのマイグレーションや、外部システムとのデータ連携(CICSやDB2、あるいは他言語で作られた電文フォーマットとのやり取り)において、構造体のレイアウト一致は死活問題です。
以下のPL/Iコードを見てみましょう。
1
Dcl 1 MY_RECORD ALIGNED,
2 ID_NO FIXED BIN(31,0) ALIGNED, / 4バイトの数値 /
2 WORK_ID CHAR(3) UNALIGNED, / 3バイトの文字列 /
2 FILLER CHAR(1) UNALIGNED; / 1バイトの予備 /
このコード、一見すると「4バイト + 3バイト + 1バイト = 合計8バイト」の構造体に見えますよね。
他の言語の感覚や、COBOLのレコ-ドレイアウトのノリで「よし、これで8バイトの電文だな」と判断してしまうと、大事故(データズレ)に繋がることがあります。
なぜなら、`FIXED BIN(31,0) ALIGNED` は「4の倍数のアドレスから始めなさい」という厳格なルールを持っていますが、その直後にある `CHAR(3) UNALIGNED` が、そのルールやメモリの境界線を微妙に揺らしてしまうことがあるからです。さらに、コンパイラの最適化オプションや、`ALIGNED` が構造体全体にどう波及するかによって、意図しないパディング(調整用の空白バイト)が自動挿入されることがあります。
—
5. パフォーマンスとデータ互換性のバランスを取るための実務知見
システムアーキテクトとして現場を渡り歩いてきた私から、初心者の方へいくつか実践的なアドバイス(知見)を授けましょう。
1. 「なんとなく動く」で放置しない
PL/Iコンパイラは非常に優秀なので、データ属性が多少ちぐはぐでも、自動的に型変換やパディングを行って辻褄を合わせてくれます。しかし、それがかえって「バッチ処理のCPU使用率高騰(オーバーヘッド)」の原因になります。
2. 外部連携ファイルや電文(COPYBOOK等)は属性を明示する
COBOLのCOPY句から自動生成されたPL/IのINCLUDE構造体などを扱う際は、`ALIGNED` なのか `UNALIGNED` なのかが混在しがちです。特に数値型(`FIXED BIN` など)と文字型(`CHAR`)が混在するレコードレイアウトでは、予期せぬパディングバイトが入らないよう、コンパイラオプションやソースコード側で明示的に制御することが鉄則です。
3. 怖がらずにストレージ・ダンプ(Core/Snap)を見よう
もし「どうも他システムとの間でデータの受け渡しがズレるな…」と感じたら、恐れずにストレージ・ダンプを覗いてみてください。メモリ上でCHARACTER変数がどのように並び、どこにパディングの隙間が空いているのかが目で確認できるようになると、もうPL/Iのメモリ管理で怖いものはありません!
—
おわりに
いかがでしたでしょうか?
「CHARACTER属性の内部表現とアライメント」という、一見すると堅苦しい技術用語も、メモリという「お部屋の収納」に例えてみると、少し身近に感じられたのではないでしょうか。
レガシーシステムの言語は、どれもハードウェアの制約と限界ギリギリまで戦ってきた先人たちの知恵と工夫の結晶です。PL/Iのデータ構造も、怖がる必要は全くありません。一つひとつ、その背景にある「なぜそうなっているのか」を紐解いていけば、必ず頼もしい相棒になってくれます。
あなたのメインフレーム開発・移行の旅が、実り多い素晴らしいものになるよう、これからも応援しています!
