メニューボタン
IBMi海外記事2021.12.08

定数の畳み込みを気に掛けるべき理由

Dawn May 著

定数の畳み込みは、コンパイラー最適化技法の1つであり、定数が含まれている演算を、可能であればコンパイラーが結果値に置き換えるという技法です。定数の畳み込みは最新のコンパイラーでは一般的であり、RPGリファレンスによれば、RPGコンパイラーはこの技法を使用しています(たとえば、 %div および %rem 関数についてのドキュメントを参照)。(訳注:定数畳み込み

しかし、読者の皆さんも私も、コンパイラーを書くことはありません。書くのは、ビジネス アプリケーションです。では、どうして、定数の畳み込みを気に掛ける必要があるのでしょうか。これは、じっくり考えてみる価値のある問いです。

「暗黒時代」には、どのようにRPGを書く必要があったかを考えてみましょう。

C                     MOVE CUSTNR    CUSTSV  50

ここでは、顧客アカウント番号を保管フィールドへコピーしています。おそらく、コントロール ブレークをチェックするためです。このコードではどのような問題が起こり得るでしょうか。単にこういうことです。すなわち、誰かが顧客アカウント番号フィールドのサイズを増やした場合に、CUSTSVは適正値へセットされなくなります。さらに悪いことには、顧客に100000以上のアカウント番号が割り当てられるまで、エラーは表面化しません。そして、そうなるのは、プログラム変更の後、かなり時間が経ってからのことかもしれません。

そのため、IBMの優秀なスタッフたちは、代わりに、以下のようにするよう促しました。

C           *LIKE     DEFN CUSTNR    CUSTSV
C                     MOVE CUSTNR    CUSTSV

エラーの源となり得るものが1つ取り除かれています。当時、私が作業していたRPGコンパイラーに、IBMが*LIKE DEFN命令コードを追加したとき、私は大喜びしたものです。

今日では、定義仕様書で同じことを行います。

dcl-s  Cust_Account_Save      like(Cust_Account);

あるいは、少なくともそうするべきです。プログラマーがLIKEキーワードを使用できたのにそうしなかったコードを数多く見てきましたが、それは古いコードについての話ではありません。

LIKEキーワードには、定義されている変数のサイズを増減させる2つ目の引数があります。たとえば、

dcl-s  Limit           packed (7: 2);  
dcl-s  NewLimit        like(Limit: +2);

NewLimitは9桁の長さとなるように定義されます。そのうちの2桁は小数部の桁です。

また、IBMは、データ定義についての情報を取得する3つの組み込み関数も提供しています。すなわち、%SIZE、%LEN、および%DECPOSです。これらの素晴らしい関数は、コンパイラーが定数の畳み込みを使用するさらに多くの可能性を生み出します。 LIKEキーワードと同様に、それらの関数は十分に活用されておらず、真価が認められていないようです。

たとえば、文字ストリングから10進数を取得する必要があるとします。%DEC関数は、簡単にそのようなタスクを実行しますが、結果の値の定義方法を知っている必要があります。このようにすることができます。

dcl-s  Limit           packed (7: 2);

Limit = %dec (TextValue: 7: 2);

しかし、こうした方がより適切です。

dcl-s  Limit           packed (7: 2);

dcl-c  Length          const( %Len    (Limit) );
dcl-c  DecPos          const( %DecPos (Limit) );

Limit = %dec (TextValue: Length: DecPos);

仮にLIMITの定義が変更された場合でも、このコードはその変更に楽々と対応できます。

%LENおよび%DECPOS関数呼び出しを、直接、%DEC関数に入れることができないというのは、私には奇妙に思われますが、そうなっているようです。代わりに、2つの名前付き定数を宣言するという、遠回りの方法を使用する必要があります。こうした挙動については「 RPGリファレンス」で説明されているとしても、これは難しいやり方だと思います。明らかに、マニュアルを読むことよりも、もっと大事な行うべきことがあるからです。

また、これらの関数は、データ タイプを変更する際にも重宝します。たとえば、固定長の文字フィールドと同じ量のデータを格納するvarchar変数が必要だとします。LIKEキーワードを使用することはできませんが、以下のようにすることができます。

dcl-s  Address      char(20);
dcl-s  AddrMod      varchar(%size(Address));

一部のコンパイラーは、定数の使用に関して、RPGコンパイラーに比べてより寛容です。たとえば、変数をデータベース フィールドの2倍の長さにする必要があるとします。おそらく、その変数にそのフィールドの16進表現を格納するかもしれませんし、それぞれのアポストロフィーを2つのアポストロフィーに置き換えるかもしません。一部のコンパイラーでは、次のようなものも許容されるでしょう。

dcl-s  Address      char(20);
dcl-s  AddrMod      char(%len(Address)* 2);

RPGコンパイラーはそうではありません。代わりに、別の方法を見つける必要があります。たとえば次のような方法ですが、私にはその場しのぎの解決策に思えます。

dcl-s  Address      char(20);
dcl-ds AddrMod;
   *n  like(Address);
   *n  like(Address);
end-ds AddrMod;

LIKEキーワードやデータ定義組み込み関数をより多く使用できればできるほど、コンパイラーが定数の畳み込みを使用する機会が増えることになります。しかし、どうして、定数の畳み込みを気に掛ける必要があるのでしょうか。最適化されたオブジェクト コードである必要があるからでしょうか。これは、もっともな理由ではありますが、さらによりもっともな理由があります。すなわち、定数の畳み込みが行われていることにより、プログラムを修正したときに、プログラムがブレークする可能性が低くなるからです。皆さんにも私にも、バグを探すことよりも、もっと大事な行うべきことがあるのです。

あわせて読みたい記事

PAGE TOP