動的配列の領域確保は事前に行うと速い
VBAの動的配列は領域の確保をRedimステートメントで上限値を指定する方法と、領域の拡張を行うRedim Preserveステートメントを指定する方法があります。
よく言われるのが、「領域の再確保(Redim Preserve)は遅いので、先に要素数を調べて領域の確保は最初に1回だけにしましょう」という話です。
これ自体はその通りで、Redim Preserveステートメントを行うと、拡張後の領域を探して元の領域から新しい領域にデータがコピーされて以降は新しい領域を利用する、といういくつもの段階があることから、時間が掛かる処理のため避けられるなら避けた方が処理速度の向上につながります。
では、Redimを事前にやっておくとどれぐらい速いのでしょうか? また、遅いと言われるRedim Preserveステートメントは、どれぐらい遅いのでしょうか?
Redimは常に高速
以下のコードは100万件のデータ領域を事前にRedimステートメントで確保します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Sub RedimTest() Dim ar() Dim iCount Dim i Dim a, b a = Timer iCount = 1000000 ReDim ar(iCount) For i = 0 To iCount ar(i) = "aaaaaaaaaaaaaaa" Next b = Timer Debug.Print b - a & "秒" End Sub |
100万件の領域を確保したあとにループしていますが、処理時間は0.1秒前後です。
これぐらいであれば体感では全くわからず、高速です。
Redim Preserveは10万件を境に遅くなる?
PCの性能によって結果は異なると思いますが、指定回数ループしながらRedim Preserveを行う以下のコードで、ループ回数を変更しながらどれぐらい処理速度に変化があるのかを計測してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Sub RedimPreserveTest() Dim ar() Dim iCount Dim i Dim a, b a = Timer ReDim ar(0) iCount = 10000 For i = 0 To iCount ReDim Preserve ar(i) ar(i) = "aaaaaaaaaaaaaaa" Next b = Timer Debug.Print b - a & "秒" End Sub |
結果は以下の通りになりました。
回数 | 秒数 | 差 |
---|---|---|
10000 | 0.01464844 | 0 |
20000 | 0.0146484 | 0 |
30000 | 0.015625 | 0.0009766 |
40000 | 0.0307617 | 0.0151367 |
50000 | 0.0561523 | 0.0253906 |
60000 | 0.06591797 | 0.0097657 |
70000 | 0.078125 | 0.012207 |
80000 | 0.1191406 | 0.0410156 |
90000 | 0.1411133 | 0.0219727 |
100000 | 0.1870117 | 0.0458984 |
200000 | 0.717041 | 0.5300293 |
300000 | 1.64209 | 0.925049 |
400000 | 3.041016 | 1.398926 |
500000 | 4.679199 | 1.638183 |
600000 | 6.775879 | 2.09668 |
700000 | 9.109131 | 2.333252 |
800000 | 11.89917 | 2.790039 |
900000 | 14.97095 | 3.07178 |
1000000 | 18.43994 | 3.46899 |
1100000 | 22.47705 | 4.03711 |
1200000 | 26.90698 | 4.42993 |
1300000 | 31.37695 | 4.46997 |
1400000 | 36.33789 | 4.96094 |
1500000 | 41.87744 | 5.53955 |
1600000 | 47.65527 | 5.77783 |
1700000 | 53.69873 | 6.04346 |
1800000 | 60.47314 | 6.77441 |
1900000 | 67.13721 | 6.66407 |
2000000 | 74.38184 | 7.24463 |
このグラフの通り、10万件ぐらいまでは1秒未満程度ですが、そこを超えると処理秒数が上がっていきます。
100万件では20秒近くまで掛かっています。
件数が少ない場合はRedim Preserveを使っても影響は少ない
上の表やグラフの通り、配列の要素数が10万件未満の場合であれば、Redim Preserveを使っても処理速度にあまり影響がありません。
1万件程度のループであれば要素数を無理に取得して配列の要素数を事前決定するよりも、ループ内でRedim Preserveしてもほとんど問題がないでしょう。
私のPCでは10万件が境目になりましたが、環境によって異なると思います。