こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語や基幹言語の経験がある方にとって、突然目の前に現れるPL/I(ピーエルワン)という言語は、どこか古めかしく、厳めしい要塞のように感じられるかもしれませんよね。
「なんだか変な書き方が多そう…」「メモリとかバッファとか、ハードウェアに近い話は難しそう…」と身構えてしまうかもしれませんが、どうぞ安心してください。一見すると独特なPL/Iのルールも、本質を一つずつ紐解いていけば、実はとても合理的でプログラマに優しい仕組みになっているんです。
今回は、PL/Iの基本構文における「予約語を持たない自由な気質」に少しだけ触れつつ、STREAM(ストリーム)入出力における `BUFFERED`(バッファード) と `UNBUFFERED`(アンバッファード) の性能差と、知られざるバッファ管理の奥深い世界について、現場のシニアアーキテクト目線で優しく解説していきますね。
—
1. ちょっと一服:PL/Iには「予約語」がない?
本題に入る前に、PL/Iのユニークな性格を象徴するトリビアを一つ。
JavaやCOBOLには、`if`や`class`、`MOVE`や受渡項目名に使えない「予約語」がたくさんありますよね。しかし、PL/Iには言語仕様上の厳密な予約語が存在しません。
どういうことかと言うと、極端な話、以下のようなコードを書くことも理論上は可能です。
1
/ ぶっ飛んだ変数名の例 /
DECLARE IF FIXED BINARY(31);
IF = 10;
「えっ、`IF`って条件分岐のキーワードじゃないの!?」と思いますよね。
PL/Iのコンパイラは、その単語が置かれた「文脈(コンテキスト)」を見て、「あ、ここは変数名だな」「ここは制御構文だな」と賢く判断します。そのため、他の言語からの移行者は「うっかり予約語と被ってコンパイルエラーになった!」というストレスから解放されます。
もっとも、実務の現場でこんな紛らわしい変数名をつける人は怒られてしまいますので、「PL/Iはそれくらい柔軟で懐の深い言語なんだな」とリラックスして捉えてもらえればOKです!
—
2. STREAM入出力の主役:BUFFERED と UNBUFFERED の違い
さて、ここからが今回のメインテーマです。
メインフレームでファイルやプリンタ、コンソールなどに対してデータを文字(文字ストリーム)として読み書きする際、STREAM入出力という方式を使います。
この時、ファイル宣言(DECLARE)で指定するのが `BUFFERED` と `UNBUFFERED` という属性です。この2つ、パッと見は地味ですが、バッチ処理のパフォーマンス(CPU使用率とI/O効率)を語る上では絶対に外せない重要ポイントなんですよ。
イメージしやすいように、身近な例えで考えてみましょう。
カート(BUFFERED) vs バケツリレー(UNBUFFERED)
- `BUFFERED`(バッファード)の世界観
倉庫(外部記憶装置)から商品を取り出すとき、一度に大きな台車(バッファ)にドサッと積んで手元(主記憶)まで持ってきます。手元に台車があるので、細かく何度も倉庫を往復する必要がありません。効率的でスピーディーですよね。
- `UNBUFFERED`(アンバッファード)の世界観
倉庫から商品を「1個ずつ」直接、手で持ってきたり持って行ったりします(バケツリレー方式)。1回あたりの動きはシンプルですが、回数が膨大になると、倉庫の扉を開け閉めするコスト(I/O負荷)が馬鹿になりません。
—
3. 性能差とシステムへの影響:なぜ使い分けるのか?
では、具体的にこの2つがシステムにどのような影響を与えるのでしょうか。
`BUFFERED` のメリット・デメリット
- メリット: まとめて読み書き(ブロック化)するため、物理的なI/O回数が激減し、全体の処理時間が劇的に短縮されます。メインフレームの限られたCPU資源やチャネルビジーを圧迫しません。
- デメリット: メモリ上に「バッファ領域」を確保するため、多少のメモリを消費します。また、出力時にバッファが溢れる(あるいはプログラムが異常終了する)直前までデータがディスクに書き込まれていないため、適切なタイミングでのフラッシュ(吐き出し)を意識する必要があります。
`UNBUFFERED` のメリット・デメリット
- メリット: バッファを介さずダイレクトにやり取りするため、データが即座に確定します。「今、この瞬間に出力したログを確実にファイルに反映させたい!」というリアルタイム性が求められるシーンで重宝します。
- デメリット: 圧倒的に遅いです。 数万件のレコードを処理するバッチでこれをやると、I/Oの嵐となり、メインフレームのCPU使用率が跳ね上がり、運用担当者から「今日のバッチ、なんでこんなに遅いの!?」と怒りの連絡が飛んでくることになりかねません。
—
4. 実践!PL/Iでのファイル宣言とコード例
百聞は一見に如かず。実際にPL/Iでどのように記述するのか、実務を想定したコードを見てみましょう。ここでは、大量の売上データを高速に処理するバッチプログラムをイメージしています。
1
/ ========================================================== /
/ STREAM入出力におけるBUFFERED/UNBUFFEREDのサンプルプログラム /
/ ========================================================== /
TEST_BATCH: PROC OPTIONS(MAIN);
/ — 変数およびファイルの宣言 — /
/ SYSIN : 入力ファイル (通常はUNBUFFEREDで十分、またはデフォルト) /
/ SYSPRINT: 出力ファイル (大量のレポート出力にはBUFFEREDが有利) /
DECLARE SYSIN FILE STREAM INPUT;
DECLARE SYSPRINT FILE STREAM OUTPUT BUFFERED; / ←ここでバッファードを指定 /
DECLARE IN_LINE CHAR(80); / 入力レコード格納用ワーク /
DECLARE EOF_FLAG FIXED BINARY(15) INIT(0); / ファイル終了検知フラグ /
/ ファイル終了時のイベントハンドラ(条件処理) /
ON ENDFILE(SYSIN) EOF_FLAG = 1;
/ — メイン処理ループ — /
OPEN FILE(SYSIN) INPUT;
OPEN FILE(SYSPRINT) OUTPUT;
DO WHILE(EOF_FLAG = 0);
/ データの読み込み /
GET FILE(SYSIN) EDIT (IN_LINE) (A(80));
/ ファイル終了チェック /
IF EOF_FLAG = 1 THEN LEAVE;
/ ここで何らかのビジネスロジック(データ加工など)が入る想定 /
/ データの書き出し(BUFFEREDのおかげでメモリ上のバッファへ効率よく蓄積される) /
PUT FILE(SYSPRINT) EDIT (IN_LINE) (A(80), SKIP);
END;
/ — 後始末 — /
CLOSE FILE(SYSIN);
CLOSE FILE(SYSPRINT);
PUT SKIP LIST(‘正常にバッチ処理が完了しました。’);
END TEST_BATCH;
コードのワンポイント解説
上記の `DECLARE SYSPRINT FILE STREAM OUTPUT BUFFERED;` の部分に注目してください。
ここにあえて `BUFFERED` と明示することで、コンパイラやOSに対して「このストリームはまとまった単位で効率よくバッファリングして処理してね」と指示を出しています。
もしここを `UNBUFFERED` に変えてしまうと、`PUT`文が実行されるたびにOSのI/Oモジュールが呼び出され、プリンタやスプールファイルへの書き込みが発生してしまいます。ログ出力のような小規模なものであれば問題ありませんが、何十万行もある帳票出力でこれをやると、システム全体のパフォーマンスを大きく損なう原因になります。
—
5. まとめ:怖がらなくて大丈夫、要は「適材適所」です
いかがでしたでしょうか?
PL/Iの「予約語がない自由な構文」や、一見とっつきにくそうな「`BUFFERED` / `UNBUFFERED`」といったストレージ・I/O属性も、その背景にあるハードウェア効率やシステム設計の思想を知れば、決して怖いものではないことがお分かりいただけたかと思います。
- 大量データをガッツリ処理するなら、効率重視の `BUFFERED`
- リアルタイム性や確実な即時書き込みが必要なら、ダイレクトな `UNBUFFERED`
基本の考え方は他の言語におけるバッファリング戦略と何ら変わりません。
レガシーシステムの世界へ飛び込んだばかりのあなたも、この「データの流れとバッファのイメージ」を頭の中に持っておけば、パフォーマンスチューニングの現場で必ずや強力な武器になるはずです。
一つずつ、焦らず確実に自分のものにしていってくださいね。応援しています!
