趣味で書いたり、完全に一人で仕事してる人を除いて、組織の中でSASを書くというのは常に何かしらルールに縛られるものですよね。
コードは共有物であったりするわけで、やれハッシュオブジェクトやFCMPやSQLやと様々なやり方を組み合わせてコードを書くと仮にそれが処理上の最善手であったとしても「おい、他のメンバーが読めないもの書くな」ってなるわけです。当然です。
新しい書き方を導入するには、何故それを使うべきなのか、メリットデメリット、学習コスト等を偉い人に説明して、OKもらったら、メンバーにも説明しなきゃならないわけですね。
DS2プロシジャは、個人的には中々イケてる仕組みだと思いますが、メリットをうまく説明して導入にOK貰うのが大変そうだな~って思いました。学習にかかる時間はやっぱりそれなりに大きそうですからね。
で、メリットを説明するのに、HadoopがとかJSONがとかって言っても、うちそんなん使ってないからとか、それなに?で終わる上司もいる気がするし、FedSQLが便利なんですといったって、パススルーでsql飛ばせるだろ、libnameエンジンでいけるだろうと言ってきそうだし、パッケージだメソッドだオブジェクトだと言ったところで、何それ?全部SASマクロで頑張れるだろうって言われちゃうかもなわけです。
まあ、それはそれでいいんですけどね。こっちも、好き勝手DS2で書けたら楽しそうだな~っていう程度の気持ちで薦めてて、細かい理由は後付けだったりするんで。
で、僕が思うのは、そういうごり押ししたい時の簡単な説明として、難しい話はせず、ポイントも1点に絞ってみるのはどうでしょう。以下の感じです。
マルチスレッド処理が簡単に書けて、処理時間がすげー短くなって、効率いいっすよ。
マルチスレッドがわからない?ひとつの処理を後ろでいくつかに分けて同時並行で処理して後で統合するんですわ。シュミレーションとかもいっぱい回せますよ!っていう切り口はどうでしょう?
なんでかというとSPD Serverとか使ってない限り、SASでマルチスレッドの処理書くのってすげー大変なんですね(対応してるプロシジャ除く)。そして、古い人ほどそれをよく知ってるからです。
threadはあくまでDS2でできることの一側面に過ぎないし、それだけでブレイクスルーになるか?
っていう意見はわかりますが、まあいいじゃないですか。入れてしまえばこっちの勝ち?なんだから。
あ、ちなみに全部冗談ですからね。
さて本題、以下のデータセットがあるとします。
data A;
array ar{100};
do i= 1 to 10000000;
do j = 1 to 100;
ar{j}=rand('uniform');
end;
output;
drop i j;
end;
run;
100万obsの100変数です
普通のSASの書き方で、セットします。
data A1;
set A end=eof;
count+1;
if eof then put count "obs読み込みました";
drop count;
run;
ログで処理時間を見てみます。
んん?結構かかってますね。まあSAS on Demandじゃなくて製品版ならもっと早いはずですけどね。
同じことを普通にDS2で書いて見ます
proc ds2 libs=work;
data A2/overwrite=yes;
dcl double count;
drop count;
method run();
set A;
count +1;
end;
method term();
put count 'obs読み込みました';
end;
enddata;
run;
quit;
ログで処理時間を見てみます。
すでにだいぶ早いなぁ、海外のPaperとか見る限り、シングルの処理で書いたら通常ステップと同じくらいのはずなんでけどなぁ。
まあいいや。
次にマルチスレッドで処理するために、一旦処理をthreadというもので定義します。
これはpakageと同じ概念で、一旦workや永久ライブラリに処理内容をデータ化したものをおきます。
proc ds2 libs=work;
/*スレッド処理定義部分*/
thread th/overwrite=yes;
dcl double count;
drop count;
method run();
set A;
count +1;
end;
method term();
put 'スレッド番号' _threadid_ 'が' count 'obs読み込みました';
end;
endthread;
run;
quit;
上記の実行は0.0何秒で終わります。実際に処理をしているわけじゃなく、処理内容を定義しただけだからです。
そして以下のコードで上で定義したthreadを呼び出して展開します
proc ds2 libs=work;
/*スレッド処理の呼び出し*/
data A3(overwrite=yes);
dcl thread th t;
method run();
set from t threads=3;
end;
enddata;
run;
quit;
ここで注目はthreads=3のところで、ここで実際に分割する数を指定して見ます。
で結果、
「スレッド番号〇がXXXXobs読み込みました」
に注目です。全部で100万の処理を、SASが適当に分けてやったことが確認できます。
(この分け方はSASが適当に決めるので毎回変わります)
処理時間も18秒が11秒なので、4割近く減ってます。ただのsetですらね。
ただ、ちょっと注意なのはSAS on demandはブラウザ経由でどっかのサーバーで回してるから
実行する時によって結構時間にムラがあります。
うまく差が出た時のスクショとっておきましょう。逆転することがざらにあるので、、。
あと調子悪いと実行終わらなかったりするので、その時は10万くらいでやってみましょう。
まあ、本来こういうでかい処理はon demandでやるもんじゃないよね。
コードの説明を少しだけ
スレッド処理定義部分の
thread th/overwrite=yes;
は、以下の処理をthというスレッド処理としてworkに保存しますよ、もう一回実行したら
上書きしますよって意味です。
_threadid_ は、自動変数で、実際実行された時、のスレッド番号が0から振られます
スレッド処理の呼び出しの方で dcl thread th t; でthにtという名前を振って定義しています
そしてスレッドを使ってデータを取得する場合 set from として、次に定義した t で threads=3;です
かなり簡単じゃないですか?
今回はただのsetですが、ds2がわかっていれば基本どんな処理でもかけるので、大規模データ処理やシュミレーションに持って来いです。
まあ、マルチスレッドを活かすにはやっぱりハード性能もそこそこ要りますが、そこは後でこっそり言っておきましょう。
100万obsじゃなく、1000万obsですね。
返信削除ちょっと手元のPCで試してみました。
環境は
OS:Win8.1 64bit
CPU:Core-i5 3.3GHz(4コア4スレッド)
メモリ:16GB
ディスク:SSD 256GB
SAS:SAS9.4TS1M3
決して遅くないPCです
まず、データセットAを作成するのに
処理時間 27.09 秒
CPU時間 26.95 秒
おっと、SAS on Demandの方が早いですね。
で、通常のsetは
処理時間 42.83 秒
CPU時間 9.15 秒
続いてds2でのシングルスレッド処理
処理時間 46.39 秒
CPU時間 23.53 秒
続いてds2での2スレッド処理
処理時間 44.19 秒
CPU時間 25.04 秒
続いてds2での4スレッド処理
処理時間 43.56 秒
CPU時間 24.43 秒
あれ?全然早くなってないですね(笑
もっとハード性能が必要なんでしょうか?
ちなみに、プログラムは「proc ds2 libs=work;」→「proc ds2;」とした以外はそのまま使用させていただきました。