#31 | 文字列結合処理を考える | |||||||||||||||||||||||
メモリ構造などを頭に入れながらプログラミングしなければならないような他のプログラミング言語に比べて、BASICは何の気兼ねなく文字列結合などの文字列処理を行うことができます。一般的に使われる文字列(String)型の「可変長文字列」では、一応最大2GBという制限はありますが、実質的にはその長さをまったく気にせずプログラミングできるといってもよいでしょう。 Sub StrTest1() Dim strData As String Dim iintLoop As Integer '文字列変数を空にする strData = "" Debug.Print Timer '1回目の2万字を追加 For iintLoop = 1 To 20000 strData = strData & "A" Next iintLoop Debug.Print Timer '2回目の2万字を追加 For iintLoop = 1 To 20000 strData = strData & "A" Next iintLoop Debug.Print Timer '3回目の2万字を追加 For iintLoop = 1 To 20000 strData = strData & "A" Next iintLoop Debug.Print Timer End Sub このコードの実行結果を下表に示します。 想像通りの結果となりました。何万回文字を追加しようと、それぞれに追加される文字数は常に"A"の1文字だけです。それにも関わらず、追加する元の文字列変数が長ければ長いほど、大きな処理時間を要していることが分かります。 続いて、文字列結合のパフォーマンスを向上させるための方策を検討してみましょう。上記の結果から、ポイントとしては次の3つが考えられます。
まず、1と2は個別に考えるのは難しいので、両方を織り込んだ2種類のコードを考えてみました。 Sub StrTest2_1() Dim strData As String Dim iintLoop As Integer strData = "" Debug.Print Timer For iintLoop = 1 To 20000 strData = strData & "AAA" Next iintLoop Debug.Print Timer End Sub Sub StrTest2_2() Dim strData As String Dim strData1 As String Dim strData2 As String Dim strData3 As String Dim iintLoop As Integer strData = "" Debug.Print Timer For iintLoop = 1 To 20000 strData1 = strData1 & "A" Next iintLoop For iintLoop = 1 To 20000 strData2 = strData2 & "A" Next iintLoop For iintLoop = 1 To 20000 strData3 = strData3 & "A" Next iintLoop strData = strData1 & strData2 & strData3 Debug.Print Timer End Sub StrTest2_1プロシージャでは、1回当たりに追加する文字数を3倍にすることによって、追加処理回数を1/3に抑えています。 一方、StrTest2_2プロシージャでは、ループの回数は前と同じ2万回×3回ですが、1つの文字列に6万字すべてを追加するのではなく、別の3つの変数を用意し、それぞれに2万字ずつ追加し、最後にそれら3つの変数どうしを結合することによって、最終的に6万字の変数を生成しています。 その結果は以下のようになりました。 最初のテスト結果の27.24秒に比べれば、いずれも大幅に実行時間が改善されています。このことから、「文字列結合においては、その回数および結合される側の変数の長さが短いほど、処理時間を短くできる」ということがいえます。 最後に、3の案をトライしてみましょう。今回は、実際にはレアケースかもしれませんが、あらかじめ6万字が追加されるということが分かっています。そこで、次の2つの方法で、あらかじめ6万字分のメモリ領域を用意しておき、そこにMid$関数を使って1文字ずつ追加する文字をその変数エリアに当てはめていくという処理を行ってみます。
Sub StrTest3_1() '長さ6万の固定長文字列変数を使う Dim strData As String * 60000 Dim ilngLoop As Long strData = "" Debug.Print Timer For ilngLoop = 1 To 60000 Mid$(strData, ilngLoop, 1) = "A" Next ilngLoop Debug.Print Timer End Sub Sub StrTest3_2() '可変長文字列変数にあらかじめString$で6万字のNullを代入しておく Dim strData As String Dim ilngLoop As Long Debug.Print Timer strData = String$(60000, vbNull) For ilngLoop = 1 To 60000 Mid$(strData, ilngLoop, 1) = "A" Next ilngLoop Debug.Print Timer End Sub ※参考までに、もしあらかじめ6万字というようにその文字数の上限が明確でない場合には、「多少余裕を持った長さでvbNullをセットしておき、最後にInstr関数を使って最初のvbNullの位置を調べ、Left$関数で切り出す」という方法を使うことができます。 この結果は以下のとおりです。 この結果から、"文字列を結合するのではなく当てはめる"という手段は、文字列処理において絶大な効果を上げることが分かったと思います。実は、Mid$関数を使った場合には、その任意の位置に文字を代入する際に、別の領域に変数の内容がいったんコピーされるという内部動作は行われません。その領域に置かれたままで処理が行われるのです。このことからも、いかに文字列変数のコピーという動作がパフォーマンスを下げる要因になるか、そしてできる限りコピー動作を行わせない工夫がパフォーマンスの向上につながるかということが分かったと思います。 |
||||||||||||||||||||||||
|
Copyright © T'sWare All rights reserved |