処理をベタ書きするか、別ラベルにまとめるか、別ソースに分けるか、という話
プログラムを書いているとき、何度も繰り返し使うコードはまとめたくなります。
SASでももちろん同じで、SASマクロにして繰り返し使うことで、コードの可視性が上がり、コーディングのミスを減らすことができます。
最近引き継いだSAS/AFのsclソースプログラムを編集していて、繰り返し使われているコードが何度も出てくるところがあったので、まとめようか悩んで、パフォーマンス的にどうなのか、というところを検証してみました。
実際のコードは以下のような感じです。
Excelにデータを投げる前に、Excelで処理できない文字を変換しておく、というものです。
_data=ktranslate(_data,"'",'"'); _data=ktranslate(_data,"’",'”'); _data=ktranslate(_data,"’",'“'); _data=ktranslate(_data,"’",'゛');
テストパターンは以下の4つを設定しました。
- パターン1:同一ラベル内でベタ書き
- パターン2:同一ソース内で別ラベルに記述してlinkする
- パターン3:別ソースに記述してcallする
- パターン4:同一ラベル内に繰り返し分ベタ書き
テスト条件は以下の通りです。
- テストコードを1000000回実行した時間を計測する(パターン4では1000000回分の処理がソース内に記述されている)
- 上記4パターンをローカルとリモート(ネットワークドライブ上)で実行する
- 10回ずつcallする。(100回やってもほとんど違いがなかったので10回で十分です)
詳しいソースは以下から参照してください。
簡単に解説すると、SAS/AFのsclでは初回実行時に処理されるinitラベルというものがあり、今回はそこに処理を記述します。
- パターン1では、doループ内にコードを記述しています。
- パターン2では、doループ内で、別のラベルブロック(test:)にlink(vbaでいうcall)しています。ローカルのユーザ定義プロシージャのようなものですね。
- パターン3では、entry渡しで、他のsclに処理をさせています。globalのユーザ定義プロシージャとして動きます。
- パターン4では、SASマクロで記述されています。実はマクロでsclのソースを書くことができるんです。コンパイル時にマクロもコンパイルされ、内部ソースコードおよび中間コードが生成されます。
実行した結果は以下の通りです。
まず、統計量をみてみると、リモートとローカル間での違いは殆どありません。
これは、使用しているカタログがメモリ上にキャッシュされているからだと思われます。
続いて、グラフを見るとパターン4 < 1 < 2 << 3 という結果が分かります。
(参照線は平均値ですが、最小にしても最大にしても参照線の位置関係は同じでした。)
ベタ書きが一番速く、続いて同一ソース内のラベル呼び出し、そして別ソースをcallすると遅くなる、という結果になりました。
パターン1と4の差はdoループによるオーバーヘッドでしょうか。差はごく僅かですが。
ただ、以下を見ると分かる通り、コンパイル時のサイズが膨れ上がっているので、パターン4はオススメはしません。
10個くらいのループならいいかもしれませんが、所詮差は僅かなので、普通にdoループで十分だと思います。(100万回でこれですから)
結論:
SAS/AFにおいて、繰り返し処理するものは同一ソース内に別ラベルで処理をまとめる。
時間があれば、CATCACHEオプションでキャッシュしていないカタログでの処理時間との違いをやってもいいかもしれないです。