前のページ|次のページ

効率的なマクロの作成

マクロの有効利用

マクロを使用して定数テキストのみを生成するアプリケーションは、効率的ではありません。通常、そのような状況では、%INCLUDEステートメントの使用を検討してください。%INCLUDEステートメントは、最初にコードをコンパイルする必要がありません(即座に実行されます)。そのため、マクロを使用するよりも効率的です。そのコードを一度だけ実行する場合、特に効果的です。詳細については、%INCLUDE Statement (SAS ステートメント: リファレンス)を参照してください。
同じコードを繰り返し使用する場合は、マクロを使用したほうが効率的です。これは、マクロが、SASジョブの実行中に何度呼び出されたとしても、一度しかコンパイルされないためです。

ユーザー名スタイルマクロ

マクロの呼び出しには、ネームスタイル、コマンドスタイル、ステートメントスタイルの3種類があります。これら3つのうち、ネームスタイルが最も効率的です。これは、ネームスタイルマクロが必ず%で始まり、ワードスキャナに対して、マクロプロセッサにトークンを渡すよう即座に指示できるためです。他の2種類の場合、ワードスキャナは、マクロプロセッサにトークンを送信するべきかどうかを即座に知ることはできません。そのため、トークンを送信するかどうかをワードスキャナが判断している間に時間が経過します。

ネストされたマクロ定義の回避

他のマクロ内でマクロ定義ネストすることは、通常は必要なく、効率的でもありません。ネストされたマクロ定義を含むマクロを呼び出すと、マクロプロセッサによって、ネストされたマクロ定義がテキストとして生成され、入力スタックに配置されます。次に、そのマクロ定義は、ワードスキャナによってスキャンされ、マクロプロセッサによってコンパイルされます。変更しないマクロの定義をネストしないでください。外側のマクロが実行されるたびに、同じネストされたマクロがマクロプロセッサによってコンパイルされます。
原則としてマクロは、別々に定義する必要があります。マクロのスコープをネストする場合は、マクロ定義ではなく、単にマクロ呼び出しをネストします。
たとえば、次の例では、マクロTITLEのネストされたマクロ定義が、マクロSTATS1に含まれています。
/* Nesting a Macro Definition--INEFFICIENT */
%macro stats1(product,year);
   %macro title;
      title "Statistics for &product in &year";
      %if &year>1929 and &year<1935 %then
         %do;
            title2 "Some Data Might Be Missing";
         %end;
   %mend title;

   proc means data=products;
      where product="&product" and year=&year;
      %title
   run;
%mend stats1;

%stats1(steel,2002)
%stats1(beef,2000)
%stats1(fiberglass,2001)
マクロSTATS1が呼び出されるたび、マクロプロセッサはマクロTITLEの定義をテキストとして生成し、マクロ定義を認識し、マクロTITLEをコンパイルします。この場合、STATS1は3回呼び出されます。つまり、TITLEマクロが3回コンパイルされます。このマクロのステートメントは数行しかないため、コンパイルに数マイクロ秒しかかかりませんが、ステートメントが数百行になるような大規模なマクロの場合は、かなりの時間がかかります。
TITLEは、STATS1の定義内で呼び出されているため、PRODUCTとYEARの値を使用できます。したがって、これらの値をTITLEのスコープで使用できるようにするために、TITLEの定義をネストする必要はありません。TITLEステートメントの定義に含まれる値が、STATS1の実行中に変化する値に依存していないという理由からも、TITLEの定義のネストは不要です。TITLEステートメントの定義がそのような値に依存している場合でも、定義をネストするのではなく、グローバルマクロ変数を使用することで、変更を反映することができます。
別々に定義されたマクロを、次のプログラムに示します。
/* Separating Macro Definitions--EFFICIENT */
%macro stats2(product,year);
   proc means data=products;
      where product="&product" and year=&year;
      %title
   run;
%mend stats2;

%macro title;
   title "Statistics for &product in &year";
   %if &year>1929 and &year<1935 %then
      %do;
         title2 "Some Data Might Be Missing";
      %end;
%mend title;

%stats2(cotton,1999)
%stats2(brick,2002)
%stats2(lamb,2001)
ここでは、マクロTITLEの定義がマクロSTATS2の定義の外にあるため、TITLEは、STATS2が3回呼び出されても1回しかコンパイルされません。TITLEの呼び出しがSTATS2の定義に含まれているため、この場合もTITLEは、PRODUCTとYEARの値を使用できます。
注: マクロを別々に定義するもう1つの理由は、各マクロを別々のファイルに保存して保守しやすくするためです。

マクロ変数への関数結果の割り当て

関数を評価するよりも、変数の参照を置換したほうが効率的です。したがって、頻繁に使用する関数の結果は、マクロ変数に割り当てます。
たとえば、次のマクロは、%DO %WHILEステートメントのすべての反復でマクロ変数THETEXTの長さを評価する必要があるため、非効率です。
/* INEFFICIENT MACRO */
%macro test(thetext);
   %let x=1;
      %do %while (&x > %length(&thetext));
         .
         .
         .
      %end;
%mend test;

%test(Four Score and Seven Years Ago)
より効率的な方法は、THETEXTの長さを一度評価して、その値を別のマクロ変数に割り当てることです。その後、次のプログラムに示すように、そのマクロ変数を%DO %WHILEステートメントで使用します。
/* MORE EFFICIENT MACRO */
%macro test2(thetext);
   %let x=1;
   %let length=%length(&thetext);
   %do %while (&x > &length);
      .
      .
      .
   %end;
%mend test2;

%test(Four Score and Seven Years Ago)
別の例として、%SUBSTR関数を使用して、SYSDATEの値から年を取り出すとします。コード内で繰り返し%SUBSTRを使用する代わりに、%SUBSTR(&SYSDATE, 6)の値をマクロ変数に割り当て、年が必要なときには、その変数を使用します。

システムオプションの無効化(適切な場合)

MPRINTやMLOGICなどのデバッグ用のシステムオプションは、場合によっては非常に効果的ですが、この種のシステムオプションを有効に設定して、プロダクション(デバッグ済み)マクロを実行することは効率的ではありません。プロダクションマクロの場合は、NOMLOGIC、NOMPRINT、NOMRECALL、およびNOSYMBOLGENを設定してジョブを実行してください。
ジョブにエラーがない場合にも、これらのオプションを有効にしてジョブを実行すると、オプションが必要とするオーバーヘッドが発生します。これらのオプションを無効にすることで、プログラムの実行がより効率的になります。
注: MPRINTとNOMPRINTの使い分けを判断する別の方法は、このオプションの設定をSOURCEオプションの設定に合わせることです。つまり、プログラムでSOURCEオプションを使用する場合は、MPRINTも使用する必要があります。同様に、プログラムでNOSOURCEを使用する場合は、NOMPRINTを設定して実行します。
注: 自動呼び出しマクロを使用しない場合は、NOMAUTOSOURCEシステムオプションを使用してください。コンパイル済みマクロを使用しない場合は、NOMSTOREDシステムオプションを使用してください。

コンパイル済みマクロ機能の使用

コンパイル済みマクロ機能は、以前のSASジョブまたはSASセッションでコンパイルされたマクロを、それ以降のSASジョブおよびSASセッションでアクセス可能にすることで、実行時間を削減します。つまり、これらのマクロを再コンパイルする必要がありません。コンパイル済みマクロ機能は、プロダクション(デバッグ済み)マクロにのみ使用してください。この機能をマクロアプリケーションを開発するときに使用することは、効率的ではありません。
注意:
ソースコードを保存してください。
コンパイル済みコードからソースコードを再作成することはできません。そのため、何らかの理由でコンパイル済みコードが破損する場合に備えて、ソースコードのコピーを安全な場所に保管する必要があります。後でマクロを変更しようとする場合にも、ソースのコピーを保持しておく必要があります。
コンパイル済みマクロ機能の詳細については、マクロの保存および再利用を参照してください。
注: コンパイル済みマクロ機能が生成するコンパイル済みコードは、ポータブルではありません。マクロを別のホスト環境に移動する必要がある場合は、ソースコードを新しいホストに移動して再コンパイルし、保存する必要があります。

自動呼び出しマクロの一元保存

自動呼び出し機能を使用する場合、入出力の点から見て最も効率的なのは、すべての自動呼び出しマクロを1つのライブラリに保存し、そのライブラリ名をSASAUTOSシステムオプション指定の先頭に追加することです。もちろん、任意の数のライブラリに自動呼び出しマクロを保存できます。しかし、マクロを呼び出すたびに、そのマクロが検出されるまで各ライブラリが順次検索されます。開いて検索するライブラリを1つに限定することで、マクロの検索時間を減らすことができます。
ただし、多数の自動呼び出しマクロが存在する場合、次にあげる項目に応じて、それらのマクロを論理的に分割することは理にかなっています。
  • 目的
  • プロダクションのレベル
  • サポート担当者
  • その他
この場合も、入出力が減少することに対する、使い勝手と保守性の悪化について、バランスを考える必要があります。
リスト内で連結されたすべての自動呼び出しライブラリが開かれ、SASジョブまたはSASセッションが実行されている間は開かれたままになります。自動呼び出しマクロを最初に呼び出すと、1回目で開かれなかったライブラリが再びテストされます。自動呼び出しマクロが使用されるたびに、これが繰り返されます。そのため、SASAUTOSシステムオプション指定に無効なパス名が存在すると、極めて非効率的になります。このSASの一部の無駄な処理に関する警告メッセージは、ライブラリを1つも開けない場合を除き、表示されません。
自動呼び出し機能の効率に関して、次の2つのヒントがあります。
  • マクロ以外のコードを自動呼び出しライブラリファイルに保存しないでください。
  • 各自動呼び出しライブラリファイルには、複数のマクロを保存しないでください。
これらのヒントに従わなくてもライブラリファイルは使用されて動作しますが、コードの保守作業が非常に増大し、その結果、効率が下がります。

その他の有用な効率のヒント

試すことのできるその他の効率化手法を次に示します。
  • 参照する予定のないマクロ変数があれば、それをnullにリセットします。
  • 三重アンパサンドを使用して、長い値を持つマクロ変数の追加スキャンを強制的に行います(該当する場合)。詳細については、長いマクロ変数値のコピーを1つだけ保存するを参照してください。
  • 状況に合わせて、MSYMTABMAX=システムオプションおよび MVARSIZE=システムオプションの値を調整します。通常、ディスク容量が不足している場合はこれらの値を増やし、メモリが不足している場合は減らします。MSYMTABMAXは、マクロ変数シンボルテーブルを格納できる領域に影響を与え、MVARSIZEは、個々のマクロ変数の値を格納できる領域に影響を与えます。

長いマクロ変数値のコピーを1つだけ保存する

マクロ変数には非常に長い値を格納できるため、マクロ変数を格納する方法がプログラムの効率に影響を与える場合があります。3つのアンパサンドを使用して間接的に参照することで、格納される長い値のコピーの数を減らすことができます。
たとえば、次に示すように、SASプログラムのセクションを表す長いマクロ変数値がプログラムに含まれているとします。
%let pgm=%str(data flights;
   set schedule;
   totmiles=sum(of miles1-miles20);
   proc print;
   var flightid totmiles;);
次のマクロによって、SASプログラムをRUNステートメントで終わるようにします。
%macro check(val);
         /* first version */    &val
    %if %index(&val,%str(run;))=0 %then %str(run;);
%mend check;
最初に、マクロCHECKが、パラメータVAL (%MACROステートメントで定義され、マクロ呼び出しから渡されるマクロ変数)に格納されたプログラムステートメントを生成します。次に%INDEX関数が、VALの値に対して文字列runを検索します(%STR関数を使用することで、セミコロンをテキストとして扱っています)。この文字列が存在しない場合、%INDEX関数は0を返します。%IF条件がtrueになり、マクロプロセッサはRUNステートメントを生成します。
マクロCHECKを変数PGMに対して使用するには、次のように、マクロ呼び出しでパラメータVALにPGMの値を割り当てます。
%check(&pgm)
その結果、SASはこれらのステートメントを次のように解釈します。
data flights;
   set schedule;
   totmiles=sum(of miles1-miles20);

proc print;
   var flightid totmiles;
run;
マクロCHECKは、正常に動作します。ただし、マクロプロセッサは、CHECKを実行する際に、PGMの値をVALの値として割り当てます。そのためマクロプロセッサは、CHECKを実行する間、2つの長い値(PGMとVALの値)を格納する必要があります。
プログラムを効率化するには、PGMの値をVALにコピーしないで使用するように、マクロを記述します。
%macro check2(val);  /* more efficient macro */   &&&val
   %if %index(&&&val,%str(run;))=0 %then %str(run;);
%mend check2;

%check2(pgm)
次のマクロCHECK2は、マクロCHECKと同じ結果を生成します。
data flights;
   set schedule;
   totmiles=sum(of miles1-miles20);

proc print;
   var flightid totmiles;
run;
ただし、マクロCHEKC2では、VALには、PGMの値ではなく、単にPGMという名前が割り当てらます。マクロプロセッサは、&&&VALを&PGMに置換し、次にマクロ変数PGMに格納されているSASステートメントに置換します。そのため、長い値は一度だけ格納されます。
前のページ|次のページ|ページの先頭へ