以前、ブログに書いていた
ダジャレクラウドですが、晴れてリリースとなりました。
遊び方ですが、『ダジャレ・リサーチ』というテキストボックスにキーワードを入力し、検索ボタンをクリックするという至ってシンプルなものになります。
ちなみに最近私が面白いと思ったものは『オリンパス』で検索したものです(まぁ少々ブラックですが・・・)。
ソフトウェア開発の常なのですが、このプロジェクトも遅延しましたが何とか無事にお披露目できるようになりました。
あまり赤裸々に書くのも如何なものと思われるかと思いますが、こういった経験は私自身も肥やしになるのと、あまり外には聞こえてこないもので興味深いと思いますので、適当にフィクションを入れつつ記事を書いてみます。ので以下はフィクションと思って頂ければと思います。
■ プロジェクトの目標がはっきりしているか?ぶれないか?
もともとHack4JPという震災復興プロジェクトの1つとして立ち上がりましたが、『だじゃれ』と『震災復興』というあまりにもかけ離れたお題に対して一部のメンバーが動機付けに苦労し余計な時間を費やした点が上げられます。
つまり、プロジェクトだじゃれという不謹慎なものに対して負い目を感じ、それに対して大義名分をつけようとして『開発を行う』というエンジニアの本分を忘れてしまい結局開発に手が付かなかった点があります。
結局は、『ダジャレで被災者を応援しよう』という当初の目標に落ち着き混乱が収拾されました。
■ボランティアに対するスタンスの違い
私はこういうボランティアは始めてなのですが、メンバー間のボランティアに対する認識の違いがありました。
私の中では、参加条件が『プロが無償でする』であり、その方が『出来る範囲で出来ることをする』という認識でいました。
『プロが無償でする』というのはどういうことかと申しますと速い話がボランティア(タダ)といってもいい加減な仕事をしてはいけないということで、例えますと英語ができない人が通訳のボランティアをやっては逆に迷惑でしょう。ということです。
つまりソフトウェア開発プロジェクトなら開発ができる人が開発を行うということなります。
当たり前ですが、どのようなソフトウェア開発プロジェクトも開発者だけ回りません。リーダーやプランナーの方とう様々な役割をもった方も必要です。チームとして活動する場合はそういったリーダーやプランナーとして仕事ができる人も参加する必要があるでしょう。
プロジェクトの混乱の1つにこのスタンスの違いがありました。つまり、無償で作業をするという認識は一致していたかと思いますが、「プロ」の部分が抜けておりました。先ほどの英語の例でいいますと英語がしゃべれないけど通訳のボランティアをするといった具合です。もっとも明らかにしゃべれないなのらヤメトケで済みますが、微妙な場合は線引きが難しくこれが混乱の元になりました。
『できるかどうか解らないがやってみる』というのはありかと思いますが、その場合は周りに迷惑にならない程度に『やっぱりできませんでした』とか『ここまでならできました』とかの報告が欲しいものです。
ボランティアに限った話ではないですが、厄介なのが充分な能力をもっていないが自分はできると思っていたり、『出来ません』と言えずに引くに引けないようになってプロジェクトが停滞しました。
この点のもう1つの問題が、メンバーの意識として『出来る事をする』ではなく『したい事をする→出来ないことをやろうとする』になってしまう点です。これが復興支援という大義名分と融合して混乱に拍車をかけた部分があります。当たり前ですが、ほとんどの日本人(世界の人)が東北の方の1日でも速い復興を願っています。前項とも絡んでくるのですがその思いが空回りして、したい事をプロジェクトとして実行させようとし結果として、出来ないことをやろうとすることになっていました。
■ボランティアに対するスタンスの違いとプロジェクト運営の経験不足
そもそも論としてボランティアだからモノを完成させる必要はないという認識の方もいらっしゃいました。こういう考え方自体は悪くないかと思いますが、少なくとも依頼者は完成させて欲しいと思っていますし、また、メンバー内にも完成させたいと思っている方も当然居ました。この場合は明らかに完成に向けて作業を行う必要があるかと思いますが、そういった中でご自身の意見を優先される方がいらっしゃいました。
個人の意見がプロジェクト運営上妨げになるという場合、その点については当然調整を行う必要があるでしょう。つまりある部品の開発者であったが興味を失ったので開発はもう終わりにしたいと思った場合、別の開発者に任せるようにする必要があるでしょう(本来ならある程度完成させてから抜けるのが筋だと思いますが・・・)。それをプロジェクトとして完成させる必要がないとされると周りの者が迷惑をこうむります。
■文化の違う方とのコミュニケーション
立場の違う人達が集まると波風が発生するもので、上記の認識の違いやら、果ては言葉遣いや段取り等の違いから波風が立ちました。
このプロジェクトですが見た目が簡単で面白そうなのでエンジニアでない方も入ってこられました。
それ自体は悪くはないですが、例えば、システム開発で発注者の立場の人と受注者の立場の人がボランティアで一緒になるとほぼ立場が逆転します。なぜなら発注者というのはお金という力を使って受注者をある意味支配していますが、ボランティアベースになるとお金という力がなくなるので、別の何かで開発者の方と協力しあわなければなりません。
こういったところでコミュニケーション不足が一部にありメンバーの不満が高まったこともありました。
この点については幸いにも粘り強く話しをしたら誤解であったことが解り、開発者でない方のプロジェクトに対する貢献方法(広報活動だったりプロジェクトの企画だったり)を考えることにより作業が進行できたので、1つ収穫になりました。
とまぁ色々問題が発生しましたが齢40を過ぎていい社会勉強になりました。
ADP公開一周年記念記事がまだ途中ですが、
Ver0.74のリリースを行います。
Ver0.74は、Accessでの整数のインサート時のエラーの改修と、pipe述語の実装があります。
pipe述語というのは、以前話に出ました、マルチスレッド機能の1つでパイプライン処理を実現する述語になります。
ちなみに、本リリースにに基づき、
LLPlanetsのライトニングトークで発表を行います。私を見かけた人は『ブログ見てます』と声を掛けていただければうれしかったりします。
では、pipe述語の使用例を見てみましょう。何回かやっていて最近ホットなSQLのパフォーマンスについての例になります。
関連記事1:[ADP開発日誌]SQL(JOIN)の実行パフォーマンスについて2011
関連記事2:SQLの実行パフォーマンスについて 2010
実験環境
JOINのパフォーマンス実験環境はこちらに記述しています。
実験1 素直にSQL側でjoinをさせたものを実行(再掲)
例により、SQLで素直にjoinさせてみます。以下のようなコードになります。
,$db = "DSN=Trade"
,$str = "SELECT Price.CODE, RDATE, OPEN, CLOSE, NAME FROM Price "
"INNER JOIN Company ON (Price.CODE = Company.CODE)"
,sql@($db,$str,[]).csv.prtn,next;
[ADP開発日誌]SQL(JOIN)の実行パフォーマンスについて2011の実験1と同じです。
実行時間も同じで、約119秒です。
実験2 ADP側でjoin(ネステッドループ&キャッシュ)
続いて、ネステッドループjoinをADPのキャッシュ機能を使って高速化をはかります。
,$db = "DSN=Trade"
,$price = "SELECT CODE,RDATE,OPEN,CLOSE FROM Price"
,$company = "SELECT NAME FROM Company WHERE CODE = ?"
,sql( $db, $price, [], @rec)
,pipe
,sql( $db,$company, [$rec[0]], $name)
,csv($rec,$name).prtn,next;
[ADP開発日誌]SQL(JOIN)の実行パフォーマンスについて2011の実験2-Bと同じコードになります。
実行時間ですが、約117秒となりました。実験1と比べて約1.6%程速くなっています。
実験3 ADP側でjoin(事前にマップ作成)
3つ目は、ADPでも事前にマップを作成し、joinを行うことができます。
,$db = "DSN=Trade"
,@tbl = {}
,sql($db, "SELECT CODE,NAME FROM Company",[], @r)
,@tbl = @tbl + [ $r["CODE"] | $r["NAME"] ]
,next
,sql($db, "SELECT CODE,RDATE,OPEN,CLOSE FROM Price",[],@rec)
,$key == $rec["CODE"].str
,csv($rec,$tbl[$key]).printn,next;
[ADP開発日誌]SQL(JOIN)の実行パフォーマンスについて2011の実験3と同じコードです。
実行時間ですが、約111秒で実験1より7%ほど速くなっていることが解ります。
続いて、pipe述語を使って並行処理をさせてみます。
実験1-P 素直にSQL側でjoinをさせたものをpipe実行
実験1のコードにpipe述語を挿入しています。
,$db = "DSN=Trade"
,$str = "SELECT Price.CODE, RDATE, OPEN, CLOSE, NAME FROM Price "
"INNER JOIN Company ON (Price.CODE = Company.CODE)"
,sql@($db,$str,[]).pipe.csv.prtn,next;
実験1のコードとの違いは4行目の
,sql@($db,$str,[]).
pipe.csv.prtn,next;
のpipeという記述で、これがpipe述語になります。pipe述語で区切られたコードは並行で処理を行います。
つまり
,sql@($db,$str,[])
の部分(バックトラックの実行)と
.csv.prtn,next;
の部分は並行で動作します。
sqlの部分は、.csv.prtn,nextの実行中にバックトラックを行います。
next述語で、pipeまで戻りますと、sqlの実行を待ち(同期)データを受け取ります。
ややこしいかも知れませんが、図で示すとよくわかるかと思います。
図で、青の矢印の部分と赤の矢印の部分がそれぞれ別のスレッドになっており平行で動作しています。
pipe述語が無い場合の動作イメージは以下のとおりです。
比較してみますと分かりますが、sql述語~next述語まででループがありますが、それを2つに分けて実行するイメージになります。
UnixのシェルやWindowsのコマンドプロンプトで、|(パイプ)を使ってコマンドをつなげることがありますが、pipe述語の実行イメージはこれと同様になります。
シェルのパイプ(|)は20年以上前からあり、お手軽にマルチタスク処理を実現できるのですがプログラム言語レベルで使えるものがなく、マルチスレッドプログラムとなるとなぜかややこしくなります。
ADPではお手軽にマルチスレッドプログラムを体験して頂くため、その一つとしてパイプを実装しました。
実行時間は、約108秒で、約9%速くなっています。少しですが実験3よりも速くなっていることが解ります。
実験2-P ADP側でjoin(ネステッドループ&キャッシュ)でpipe実行
続いて、実験2のコードにpipe述語を挿入しています。
,$db = "DSN=Trade"
,$price = "SELECT CODE,RDATE,OPEN,CLOSE FROM Price"
,$company = "SELECT NAME FROM Company WHERE CODE = ?"
,sql( $db, $price, [], @rec)
,pipe
,sql$( $db,$company, [$rec[0]], $name)
,csv($rec,$name).prtn,next;
実行時間は、約89秒で実験2と比べて約24%速くなっています。
興味深いのは実験1-Pよりも速度向上が大きいです。pipe述語は半分に分割してそれぞれ実行するという方式をとっていますが、当然ですが常に半分になるとは限りません。上手く半分に分割できる場合もありますし、そうでない場合もあります。そのような関係でこのような逆転現象が発生します。一口にJOINのパフォーマンスといってもこのように様々な要因が絡んできますので、一概に『○○が効率的』といえないことを表す良い例となっています。
実験2-PP ADP側でjoin(ネステッドループ&キャッシュ)でpipe実行2
実験2-Pのコードにさらにpipe述語を挿入しています。pipe述語は1つだけでなく複数入れることもできます。
,$db = "DSN=Trade"
,$price = "SELECT CODE,RDATE,OPEN,CLOSE FROM Price"
,$company = "SELECT NAME FROM Company WHERE CODE = ?"
,sql( $db, $price, [], @rec)
,pipe
,sql$( $db,$company, [$rec[0]], $name)
,pipe
,csv($rec,$name).prtn,next;
実行時間は、約112秒で実験2-PPと比べて逆に遅くなっています。このように闇雲にマルチスレッドを行っても必ずしも速くならない場合がある(もちろん速くなる場合もある)のが面白いところです。pipe述語を2つ使うと3つスレッドが動作しますが、実験環境ではCPUコアが2つしかないので足の引っ張り合いのようなことになったようです。
実験3-P ADP側でjoin(事前にマップ作成)でpipe実行
続いて、実験3のコードにpipe述語を挿入しています。
,$db = "DSN=Trade"
,@tbl = {}
,sql($db, "SELECT CODE,NAME FROM Company",[], @r)
,@tbl = @tbl + [ $r["CODE"] | $r["NAME"] ]
,next
,sql($db, "SELECT CODE,RDATE,OPEN,CLOSE FROM Price",[],@rec)
,$key == $rec["CODE"].str
,csv($rec,$tbl[$key]).printn,next;
実行時間は、約91秒で、実験3と比べて約18%速くなっています。
ちなみに実験3-Pからさらにpipeを挿入しても良いのですが、実験2-Pの時と同様にあまり速くならないので省略します。
結論
各実験結果を示します。
pipe述語の効果
実験 | 実行時間(秒) |
実験1 | 119 |
実験2 | 117 |
実験3 | 111 |
実験1-P | 108 |
実験2-P | 89 |
実験2-PP | 112 |
実験3-P | 91 |
実験1~3どの場合でも、pipe述語が有効だということが分かります。これは、
・DBMSからデータを取得する
・ファイルへ書き出す
という2つのIO処理があり、pipe述語によって、それらを同時に実行することが出来る為です。
また実験2-PPと実験2-Pを比べても分かりますとおり闇雲にマルチスレッド化しても高速化が図れない場合もあります。
パフォーマンスアップは様々な要素が関わってきますので実験により確認しながらということが必要になります。
pipe述語はお手軽にマルチスレッドを実現でき、また取り外しも楽なので簡単に実験や試行錯誤が出来ます。
ADPのpipe述語はキャッシュ機能と同様に便利な道具として利用できるかと思います。
また、実験1-P、2-P、3-Pを比較しますとどれをとってもパフォーマンスにあまり差がないことがわかるでしょう。ADPの開発にあたりプログラマの自由度を高めるということも考慮しています。つまり、『○○でなければダメ』ではなく、どのアルゴリズムを採用するかはプログラマーの判断で、いか様にも選択できるような言語を目指しています。
追記:コメント欄での指摘およびテスト再現性を考慮してテスト環境を整備して再度計測しています。