【入門編】SEARCH関数のINDEX関数との性能比較 – PL/Iの基本構文とデータ制御実践ガイド

こんにちは!メインフレームの世界へようこそ。
JavaやCOBOLといった他のモダンな言語や基幹言語の経験がある方にとって、突然目の前に現れるPL/I(ピーエルアイ)のソースコードは、少し独特で要塞のように高くそびえ立って見えるかもしれません。

「変数名のルールってどうなっているの?」
「なんか見たことのない関数がいっぱいあるぞ……」

そんな不安を抱えていらっしゃるあなたへ。大丈夫です、怖くないですよ。一つずつ紐解けば、PL/Iは非常に合理的で、かつてIBMのエンジニアたちが頭をフル回転させて作り上げた「美しいシステム言語」であることが分かります。

今回は、そんなPL/Iの文字操作関数の中から、「INDEX関数」と「SEARCH関数」の性能の差にスポットを当ててみましょう。基幹システムのバッチ処理で何百万件もの電文やマスターデータを舐め回すように検索する時、この違いを知っているかいないかで、夜間バッチの終了時間がごっそり変わってくるんですよ。

—

1. まずは基本:PL/Iの識別子と「予約語を持たない」という懐の深さ

他の言語(JavaやCOBOLなど)を触ってきた人が最初に驚くのが、PL/Iの「予約語(Keywords)がない」という仕様です。

Javaなら `class` や `public`、COBOLなら `MOVE` や `PERFORM` といった言葉は変数名に使えませんよね。しかし、PL/Iには「これが変数名に使えない単語」という概念が(基本的に)ありません。

例えば、こんなコードが書けてしまいます。

DCL INDEX FIXED BIN(31); / なんと、関数の名前である「INDEX」をそのまま変数名にしちゃいました /

コンパイラはどうやって「これは関数名か? それとも変数名か?」を判断しているのでしょうか?
それは、「後ろにカッコ `( )` が続くかどうか」や、文脈という名の空気を読んで判断しています。まるで熟練の職人のようなスマートさですね。初めて見た時は「マジかよ!」と冷や汗が出たものですが、慣れるとこの柔軟さが心地よくなってきます。

—

2. 文字列から特定の文字を探す:INDEXとSEARCHの出会い

さて、本題の文字検索のお話です。
テキストの中から特定の文字や文字列がどこにあるかを探したい時、私たちはよくこの2つの関数を思い出します。

  • `INDEX(源流, 探す文字列)`
  • お馴染みの機能です。「あいうえお」の中から「う」を探す、といったように、部分文字列が最初に登場する位置を返してくれます。
  • `SEARCH(源流, 探す文字の集まり)`
  • ちょっと通な関数です。「指定した文字のどれか一つに最初に一致する位置」を探します。

「あれ? どっちも文字を探すなら同じじゃないの?」と思いますよね。
ここからが、メインフレームの心臓部を覗き込むようなディープで面白いお話です。

—

3. アセンブラレベルで何が起きているのか?(性能比較の核心)

JavaやCOBOLから来た方だと、「関数なんてどれも同じようにループを回して文字を比較しているんでしょ?」と思われがちです。しかし、PL/Iのバックグラウンドで動くIBMのPL/Iオプティマイジング・コンパイラは、コンパイル時に凄まじい最適化を行います。

INDEX関数の裏側:ハードウェアの超高速命令への直結

`INDEX` 関数(特に探す対象が単一文字や短い固定長の場合)は、コンパイルされると、S/370やZアーキテクチャが持つ強力なマシン語命令(アセンブラ命令)、例えば `TRT`(Translate and Test) や `CLCL`(Compare Logical Character Long) といった、ハードウェアレベルのブロック処理命令に直接置き換えられます。

CPUの回路レベルで「一気にメモリの連続領域をスキャンする」ため、余計なループのオーバーヘッドがゼロに等しいのです。

SEARCH関数の裏側:柔軟性と引き換えのコスト

一方、`SEARCH` 関数は、「この文字たちのグループのどれか」にマッチするかどうかを判定します。比較対象が複数(文字のセット)になるため、単純なハードウェア命令一発では表現しきれなくなり、コンパイラは内部的にバイト単位のルックアップテーブル(判定表)を生成するか、あるいはインラインで最適化された小さなループを展開します。

結果として、単純な「特定文字の1文字検索」であっても、`SEARCH` を使うと、内部の判定ロジックがワンクッション挟まる分、`INDEX` に比べてわずかにCPUサイクルを消費してしまうのです。

—

4. 実務でどう使い分ける?(PL/Iコード例付き)

百聞は一見にしかず。実際のバッチプログラムを想定したコードを見てみましょう。
電文データの中から特定の区切り文字や特定の値を探すシーンです。

Dcl TARGET_DATA Char(100) Init(‘ABC001-XYZ-999’);
Dcl POS Fixed Bin(31);

/ ———————————————————
ケース1: 単一の文字や文字列をガッツリ探す場合
-> ここは迷わず INDEX を使おう!
——————————————————— /
POS = INDEX(TARGET_DATA, ‘-‘); / ‘-‘ が最初に現れる位置を探す /

If POS > 0 Then
Do;
Put Skip List(‘ハイフンの位置を発見しました: ‘, POS);
/ 内部でハードウェア命令(TRT等)が走り、爆速で処理されます /
End;

/ ———————————————————
ケース2: 「数字のどれか」や「複数の候補文字」のいずれかに
最初にヒットする場所を探す場合
-> ここでこそ SEARCH の出番!
——————————————————— /
/ ‘0123456789’ という文字の集まりのどれかに最初に一致する位置 /
POS = SEARCH(TARGET_DATA, ‘0123456789’);

If POS > 0 Then
Do;
Put Skip List(‘最初に数字が登場した位置: ‘, POS);
/ 複数の候補文字を一度にスキャンできるため、自前でIF文を並べるより遥かにエレガント /
End;

アーキテクトからのワンポイントアドバイス

もしあなたが、「特定の1文字(例えばカンマ `,` やハイフン `-`)がどこにあるか」を探したいだけなのに、コードを汎用的にしようとしてうっかり `SEARCH(DATA, ‘,’)` なんて書いてしまったら……?

動くには動きますが、100万件、1000万件と回る夜間バッチの中では、わずかなCPU命令の差がチリツモで「バッチ遅延アラート」という名のモンスターを呼び覚ます原因になります。
「1文字や単純な文字列を探すなら `INDEX`」「複数の文字のどれかを探すなら `SEARCH`」という使い分けを、ぜひ心に留めておいてくださいね。

—

おわりに

レガシーシステムやメインフレームの世界は、一見すると古めかしく、冷たい呪文の集まりのように見えるかもしれません。しかし、その下で動いているのは、「いかにハードウェアの性能を極限まで引き出し、大量のデータを秒速で処理するか」という先人たちの熱い執念と知恵の結晶です。

PL/Iの構文や関数一つひとつには、必ずそう選ばれた理由(アセンブラレベルの背景)があります。
「怖くないですよ、一つずつ紐解けば簡単です」。

これからも、その好奇心を大切に、メインフレームの深淵を楽しんでいきましょう!あなたのシステム開発・移行プロジェクトがスムーズに進むよう、これからも応援しています。

タイトルとURLをコピーしました