こんにちは!IBMメインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語をご経験されてきた方にとって、PL/I(ピーエルワン)という名前を聞くだけで「なんだか古めかしくて難しそう…」「メモリとかバウンダリーとか、怖い言葉が出てきそう…」と身構えてしまうかもしれません。
でも、安心してくださいね。今日は、PL/Iが持つちょっとユニークで、かつメモリ効率を極限まで高めるための重要なお約束「UNALIGNED(アンアラインド)属性」について、一緒に優しく紐解いていきましょう。
他の言語から来た方にとっては「えっ、そんなことまでプログラマが気にするの?」と驚くような、メインフレームならではの深い世界が広がっていますよ。
—
1. そもそも「境界調整(Alignment)」ってなに?
JavaやCOBOLで構造体(COBOLなら01レベルの集団項目ですね)を定義するとき、メモリ上の並び順やパディング(隙間)について、あまり深く考えたことはないかもしれません。コンパイラやランタイムが勝手にいい感じに配置してくれていますよね。
しかし、IBMのハードウェア(Zアーキテクチャ)やPL/Iの世界では、CPUがメモリからデータを効率よく読み込むために、「偶数パリティの番地(2の倍数、4の倍数、8の倍数など)」にデータをピタッと合わせようとする習性があります。これを境界調整(Alignment / アラインメント)と呼びます。
🏠 一軒家に例えてみましょう
想像してみてください。家具(データ)を部屋(メモリ)に配置するとき、CPUという大家さんは「大きなタンス(4バイトの数値など)は、4の倍数の位置から置かないと、引き出しを開けにくいんだよねぇ」というこだわりを持っています。
そのため、もし「奇数番地」からタンスを置こうとすると、コンパイラが親切心(?)から、間に「何の意味もない無駄なスペース(パディング)」を勝手に差し込んで、位置を調整してしまうのです。
これがデフォルトの ALIGNED(アラインド) の世界です。無駄なスペースができる代わりに、CPUの読み込み速度は最速になります。
—
2. メモリをケチりたい!そこで登場するのが「UNALIGNED」
「メインフレームのメモリは高価なんだから、そんな無駄な隙間は1バイトたりとも空けたくない!」
「外部システム(C言語やCOBOL、あるいはファイルレイアウト)と1バイト単位で正確にレイアウトを一致させたい!」
そんな切実な現場の要望に応えるのが、UNALIGNED属性です。
構造体や個別の変数に `UNALIGNED` を指定すると、「CPUの効率なんて気にしなくていいから、隙間なく詰めて配置して!」とコンパイラにお願いすることができます。
実際のPL/Iコードを見てみましょう
百聞は一見に如かず。実際にどのように書くのか、サンプルコードを見てみましょう。大文字ベースの、実務でよく見かけるスタイルで記述しています。
1
/ ———————————————— /
/ 構造体の境界調整(ALIGNED vs UNALIGNED)の比較 /
/ ———————————————— /
DCL 1 MY_DATA_ALIGNED, / デフォルト(アラインド) /
5 CODE_A CHAR(1), / 1バイト /
5 COUNT_B FIXED BIN(31); / 4バイト整数(4の倍数境界に配置される) /
DCL 1 MY_DATA_UNALIGNED UNALIGNED, / 構造体全体にUNALIGNEDを指定 /
5 CODE_A CHAR(1), / 1バイト /
5 COUNT_B FIXED BIN(31); / 4バイト整数(隙間なくピッタリ詰められる) /
この宣言において、メモリ上のサイズ(LENGTH)を調べてみると、面白い違いが出ます。
- `MY_DATA_ALIGNED` の場合:
`CODE_A` の後ろに、`COUNT_B` を4の倍数番地に合わせるための3バイトのパディング(隙間)が勝手に挿入されます。そのため、合計サイズは 8バイト になります。
- `MY_DATA_UNALIGNED` の場合:
隙間風を入れる隙も与えず、1バイトと4バイトがぴったり肩を寄せ合います。合計サイズは 5バイト になります!
「たった3バイトの節約でしょ?」と思われるかもしれませんが、これが数百万件のレコードを扱う巨大なデータベースや、外部とのインターフェースファイル(レコードレイアウト)になると、数メガバイト、ひいてはギガバイト単位のメモリ・ストレージ節約につながるのです。
—
3. 節約の裏にあるトレードオフ(代償)
「じゃあ、全部 `UNALIGNED` にしちゃえば万ボヤキ解決じゃん!」と思われるかもしれませんが、世の中そんなに甘くありません。ここがアーキテクトの腕の見せ所です。
UNALIGNED属性には、明確なトレードオフ(代償)が存在します。
1. アクセス速度の低下(CPUへの負荷)
本来、4の倍数番地にあるべき4バイト整数が、奇数番地などに無理やり詰め込まれているため、CPUがそのデータを読み書きするときに「おっと、ズレているから一度分解して組み立て直さないと…」という追加の処理(アライメント例外や、複数回にわたるメモリアクセス)が発生します。
2. CPUサイクルの消費
1回で済むはずの命令が数サイクル余分にかかるため、膨大なループ処理を行うバッチプログラムなどでは、積もり積もって処理時間の遅延(バッチウィンドウの圧迫)を引き起こす原因になります。
—
4. 実務の現場におけるアドバイス
レガシー移行や新規のPL/Iプログラミングにおいて、どのようにこれらを使い分ければよいのでしょうか。現場の知見として、いくつか指針を授けましょう。
- 外部ファイル(BSAM/QSAM)やDB2のホスト変数、C言語等との連携
相手のレイアウトが1バイト単位でガチガチに決まっている場合は、迷わず `UNALIGNED` を使ってください。ここを `ALIGNED` にしてしまうと、データがずれて大惨事(文字化けや数値異常)になります。
- 純粋な内部ワークエリア(計算用など)
外部とやり取りしない、プログラム内だけで完結する一時的なワーク構造体であれば、デフォルトの `ALIGNED` に任せておくのが、CPUパフォーマンスの観点から最も安全で高速です。
—
まとめ
いかがでしたでしょうか?
「UNALIGNED」という見慣れないキーワードも、「メモリの隙間を埋めてコンパクトにする魔法の呪文、ただしCPUのひと手間で少し処理が重くなるトレードオフがある」と捉えれば、もう怖くありませんよね。
JavaやCOBOLから来た私たちにとって、こうしたハードウェアに近いレイヤーの制御は新鮮であると同時に、メインフレームならではの「リソースを限界まで絞り出す美学」を感じられるポイントでもあります。
もしバッチ改修やマイグレーションの最中に「あれ、このレイアウトなんかズレるぞ?」と思ったら、この記事の「境界調整」と「UNALIGNED」を思い出してみてくださいね。
あなたのレガシーシステムの旅が、実り多いものになりますように。それではまた!
