タイトル : Re^5: 効率的なパンデジタル数作成方法が分かりません 投稿日 : 2017/04/19(Wed) 12:02 投稿者 : 魔界の仮面弁士
全文引用はお控えください。 引用は適切に。 > Redim Preserve 使っています。と云うのもどれくらいの配列量になるのか分からなかったので一つ見付ける度に > カウントアップして配列量を増やしてやろう。と安直に考えていました。 通常は「配列」ではなく、ジェネリックコレクションなどを利用するようにします。 Redim Preserve は比較的低速な処理なので、仮に使うとしても、 ReDim Preserve の呼びだし回数をできるだけ減らすように心がけてください。 たとえば、足りなくなった時に 1 件ずつちまちま増やしていくのではなく、 もっと大きな単位(数百件〜数万件、あるいは現データの数パーセントなど)を 一括して確保するようにします。そして、ループ処理が終わったところで 多すぎた分を減らして調整するような作りにすると、かなり高速化するでしょう。 というのも、Redim Preserve というのは単なるサイズ変更ではなく、内部的には (1) 現在の配列とは別に、新しく別の配列を作成 (2) 古い配列の内容を、新しい配列にコピー (3) 古い配列を解放 という 3 段階の処理が行われているからです。 要素数が数百件程度であればさほど問題になりませんが、要素数が増えるほど、 「メモリ確保」「コピー」「解放」の負荷も増加するため、 件数に応じて処理速度が加速度的に増加してしまいます。 たとえば、下記のような実験コードを用意してみました。 ReDim Preserve を 1 万回呼び出して、その処理時間を Label に表示するサンプルです。 Dim s() As String Dim sw As Stopwatch '経過時間測定用 Dim f As Integer ReDim s(-1) '0個から増やし始めた場合 f = s.GetUpperBound(0) sw = Stopwatch.StartNew() For n = 1 To 10000 '1万個増やす ReDim Preserve s(f + n) s(f + n) = n.ToString() Next sw.Stop() Label1.Text = sw.Elapsed.ToString() ReDim s(99999) '10万個から増やし始めた場合 f = s.GetUpperBound(0) sw = Stopwatch.StartNew() For n = 1 To 10000 '1万個増やす ReDim Preserve s(f + n) s(f + n) = n.ToString() Next sw.Stop() Label2.Text = sw.Elapsed.ToString() ReDim Preserve の回数は、どちらも同じ「1 万回」であるにも関わらず、 それぞの処理時間は圧倒的に異なります。 1 回目のループは、要素数が少ないので 0.1 秒程度で完了しましたが、 2 回目のループは、要素数が多いために 2.0 秒程度を要しました。 今回は 300 万件のデータを扱う必要があるので、さらに差は広がるでしょう。 100万個から始めた場合、ReDim Preserve を 1 件ずつ、1 万回増やすのに 25 秒もの時間がかかりました。 しかし最初に 101 万個の配列を確保しておき、ループ内では ReDim を 呼ばないようにすれば、僅か 0.02 秒足らずで同じ処理結果が得られます。 これは配列に限らず、文字列連結などにも言えることです。 strData = strData & "新しい文字列" のような処理をループ内で繰り返す場合も、 文字列が長くなるにつれて、処理速度が加速度的に低下します。 (連結処理のたびに、メモリーの再確保・複写・解放が発生するため) |