自転車☆妄想日記: パフォーマンスアップの初歩の初歩 Part1

2013-03-14

パフォーマンスアップの初歩の初歩 Part1

こないだの話。全然自転車じゃない話。


あるWindowsのフォームアプリの処理速度が遅いので何とかしてくれー、という依頼があった。

早速そのアプリのソースをみると、「そりゃ遅くなるわなー。」という箇所が幾つかあったので、早速書き換え。時間を測ってみると、修正前は30秒ほどかかっていた処理が2,3秒で終了w 

依頼人(元のコード書いた人じゃないよ)は大層よろこんだので、それはそれでよかったのだが・・・・・。


ちょっと待て。


今回修正した内容は、初歩の初歩。誰でも知ってる内容だと思っていたのだが。でもよく考えてみると、他の現場でも堂々とこれを無視してコーディングしてる人がいた、ということは、知らない人は知らない(そりゃそうか)っていう知識なのか・・・・?


------------


というわけで、自転車ブログにも係わらず、プログラムについて突然語り出したPart1。
今回は、「文字列の結合の危険性」について。


VB系で、文字列の結合と言ったらこんな感じ、なんの変哲もない。


str = str & "hode"


少ない数ならこれで問題ないが、データをどこからか読み込んで、そのデータをループさせて結合していくなんて処理で使うと一度に地獄と化す。例えばこんな感じ。この例では、文字列"hode"を文字列変数strに100回突っ込むというもの。


For i = 0 To 100
  str = str & "hode"
Next



こういう処理の場合、内部でどういったことしてるのかということを考えた人が書いた記事がこれ。これはAccessの話だけど、.Netでもやってることは大差ないと思われる。

要するに、「大量の文字列データを取り扱う時に、文字列変数にどんどんぶち込んでいくやり方はNG」ということ。



---------------



ではどうすればいいのか?

VB6(VBA、VBS含む)であれば、一旦、配列(この例では、Ar)にデータを入れて、最後にJoinする、が正解。


For i = 0 To 1000
  Ar(i) = "hode"
Next
result = Join(Ar, ",") '第二引数は好きな区切り文字を入れて! 空文字・改行コードも可能。


入れるデータ数が予めわからない場合は、毎回配列をRedimしなければいけないので面倒だけど、それでも普通の文字列結合に比べると、遥かに高速。


VB.Netの場合は・・・・、勿論、List(Of String)を使う。StringBuilder?まあ、その辺りは趣味で決めていいんではないかと。処理速度にすごい違いはないようだ。個人的には、ForEachメソッドが使えるのでListを選択してしまうことが多い。



For i = 0 To 1000
  list.Add("hode")
Next
result = String.Join(",",list.ToArray()) '第一引数は好きな区切り文字を入れて! 空文字・改行コードも可能。



配列やリストを使うことによって、文字列変数の結合を使うシーンはかなり減るはず。あとはクエリ文字列を結合して生成、なんてシーンでみることがある。これは根強く残っている。例えば、こんな感じ。


str = str & " SELECT *"
str = str & " FROM USERS"
str = str & " WHERE id = '" & ID & "'"


この場合は、結合する回数が少ないのでパフォーマンスに与える影響は殆どない。問題無し・・・・・、といいたいところだが、今度はSQLインジェクションの関係で、このコードはよろしくない(三行目の変数IDでインジェクションが可能、SQLインジェクションに関しては、ぐぐって調べてください)。個人的には、可能であればストアドを作ってしまう。クエリの変更を考えると、ストアドの方が修正作業が簡単だからだ。


いろいろ試した結果、個人的には文字列変数の結合は殆ど使わなくなってしまった。そういったわけで結論は、ざっくり「どのシーンでも、変数を使った文字列の結合は、可能な限り避ける」ということでいいと思う。


では次回。