今回は64bit版VBAの整数型で使えるIntergerとLongとLongLongのうち、どれが最速なのかを調べてみました。
きっかけ
Twitterでは、定期的に「VBAの変数の型はIntegerかLongかどちらにすべきだろうか」という話が話題になります。
これに関してはLong
にするのが無難で、Microsoftは公式ホームページにてInteger型変数にパフォーマンス上の利点は無いと明言しています。
実際に多くの方々が調査しており、LongはIntegerの70%~90%の処理時間になる傾向であることが結果として現れています。
で、解決済みの案件に一太刀入れたくなるのが、私。
Excel 2019からは64bit版が推奨になったけど、今でもLong型最速説が通用するのだろうか?
実はLongLong型のほうが高速になるんじゃないか?と、考えずにはいられない訳でして。
思い立ったが吉日。試してみました。
各種整数型の仕様
今回検証するかもしれない型はこちら。
データ型 | 表記 | 消費メモリ | 最低値~最大値 | 型文字 | 利用条件 |
---|---|---|---|---|---|
バイト型 | Byte | 1バイト | 0 ~ 255 |
- | - |
整数型 | Integer | 2バイト | -32,768 ~ 32,767 |
% | - |
長整数型 | Long | 4バイト | -2,147,483,648 ~ 2,147,483,647 |
& | - |
長長整数型 | LongLong | 8バイト | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 |
^ | 64bit限定 |
システム依存の長整数型 | LongPtr | 4バイト または 8バイト | -2147483648 または -9,223,372,036,854,775,808 ~ 2147483647 または 9,223,372,036,854,775,807 |
- | 2010以降限定 |
なんか | Variant | 16バイト ~ 22バイト | なんでも | - | - |
ところで、LongLong型は64bit版のOfficeでしかコンパイルできないという問題があります。
したがって、ちゃんと32ビット版のOfficeでもコンパイルできるLongPtr
のほうが、実用性としては高いような気がします。
気になるのはLongPtrが32bitにおけるLong、64bitにおけるLongLongと同じ結果になるかどうか。ですね。
メモリ消費量の確認
速度の比較に入る前に・・・消費メモリを確認しておきましょう。
以下の結果から、メモリの消費量は仕様に記載されているバイト数と比例することが確認できました。
(Variantは若干の疑問が残りますが)
LongPtrのメモリ消費量も、仕様と同じでビット数に合わせて妥当な方を選択しているということが確認できました。
VBA
'メモリ消費量の確認 Option Explicit Const MAX = 10 ^ 4 Sub メモリ使用量Integer() Dim arr(1 To MAX, 1 To MAX) As Integer Stop End Sub Sub メモリ使用量Long() Dim arr(1 To MAX, 1 To MAX) As Long Stop End Sub Sub メモリ使用量LongPtr() Dim arr(1 To MAX, 1 To MAX) As LongPtr Stop End Sub #If Win64 Then Sub メモリ使用量LongLong() Dim arr(1 To MAX, 1 To MAX) As LongLong Stop End Sub #End If Sub メモリ使用量Variant() Dim arr(1 To MAX, 1 To MAX) As Variant Stop End Sub
結果(32bit)
タスクマネージャーの「詳細」タブにて、EXCEL.EXEの「メモリ(アクティブな以下略)」を使って、マクロの実行前とSTOP中の差分を算出しました。
Dim arr(1 To 10000) As Type | 実使用メモリKB | 仕様上消費メモリ | 1バイト当たり |
---|---|---|---|
Byte | 98,224 | 1 | 98,224 |
Integer | 195,528 | 2 | 97,764 |
Long | 390,536 | 4 | 97,634 |
LongPtr | 390,908 | 4 | 97,727 |
Variant | 1,562,656 | 16 | 97,666 |
結果(64bit)
Dim arr(1 To 10000) As Type | 実使用メモリKB | 仕様上消費メモリ | 1バイト当たり |
---|---|---|---|
Byte | 97,664 | 1 | 97,664 |
Integer | 195,432 | 2 | 97,716 |
Long | 390,628 | 4 | 97,657 |
LongLong | 781,252 | 8 | 97,657 |
LongPtr | 781,252 | 8 | 97,657 |
Variant | 2,252,052 | 22 | 102,366 |
Variantは整数時16と仕様に記載があるが、本件では22バイト消費した。(が、1バイト換算が変な数字になるのが疑問)
比較1
Forループのカウンタに割り当ててみました。
VBA
Sub t1() tStart = Timer ' Dim i As Byte, j As Byte 'ERR ' Dim i As Integer, j As Integer Dim i As Long, j As Long ' Dim i As LongPtr, j As LongPtr ' Dim i As LongLong, j As LongLong ' Dim i As Variant, j As Variant For i = 1 To 32766 For j = 1 To 32766 Next Next Debug.Print Format$(Timer - tStart, "0.00") & " sec." End Sub
結果(32bit)
\ | 所要時間(秒) | 最速を100%とした時 |
---|---|---|
Integer | 3.21 | 100% |
Long | 3.30 | 103% |
LongPtr | 3.29 | 102% |
Variant | 4.74 | 148% |
おや、これは・・・?IntegerのほうがLongより速いという結果になってしまいました。※Core i7
追記:何度やってもIntegerのほうが微妙に早かったのですが、別のPCでも試したところ同一の結果で安定しました。※Xeon E5
結果(64bit)
\ | 所要時間(秒) | 最速を100%とした時 |
---|---|---|
Integer | 6.08 | 167% |
Long | 4.88 | 134% |
LongPtr | 3.67 | 101% |
LongLong | 3.63 | 100% |
Variant | 4.58 | 126% |
64bitでは、LongLong > Long > Integerという結果になりました。
※32bitと64bitを計測したPCは性能が異なります。2つの表の時間を比較することは無意味です。
比較2
今度はカウンタはLongに固定して、たんなるインクリメントを繰り返した例で試してみます。
VBA
Sub t2() Dim cnt As Integer ' Dim cnt As Long ' Dim cnt As Single ' Dim cnt As Double ' Dim cnt As Currency ' Dim cnt As LongLong ' Dim cnt As LongPtr ' Dim cnt As Variant tStart = Timer Dim i As Long, j As Long For i = 1 To 10 ^ 5 cnt = 0 For j = 1 To 10000 cnt = cnt + 1 Next Next Debug.Print Format$(Timer - tStart, "0.00") '& " sec." End Sub
結果(32bit)
\ | 所要時間(秒) | 最速を100%とした時 |
---|---|---|
Integer | 7.76 | 127% |
Long | 7.38 | 121% |
LongPtr | 6.09 | 100% |
Variant | 14.37 | 236% |
結果(64bit)
\ | 所要時間(秒) | 最速を100%とした時 |
---|---|---|
Integer | 8.75 | 125% |
Long | 8.63 | 123% |
LongPtr | 7.14 | 102% |
LongLong | 7.02 | 100% |
Variant | 13.78 | 196% |
※32bitと64bitを計測したPCは性能が異なります。2つの表の時間を比較することは無意味です。
使い分けが必要な場面
以上で結果は分かりましたが、必ずしも統一してしまえば良いという話ではありません。
何らかの理由で使い分けたい場面が存在します。
Integer
- 16bitの値を返すAPIからデータを受け取る場合
- メモリを少しでも削減したい時
- 最大値が32767なことを利用して実行時エラーを起こしたい時
- xls形式の行数(65536)
- xlsx形式の列数(16384)
Long
- 32bitの値を返すAPIからデータを受け取る場合
- 最も互換性と汎用性が高い
- xlsx形式における最大行数に対応する時(1,048,576)
LongPtr
- bit依存の値を返すAPIからデータを受け取る場合
- 変数の容量が32bit・64bitどちらでも良い場合
- メモリに余裕がある時は、少しでも巨大データを扱えるようにしたい時
- Excel 2007以前では絶対に使わないマクロの場合
LongLong
- 64bitの値を返すAPIからデータを受け取る場合
- 明らかに巨大なデータを扱うことを予め予期している時
- 64bit環境でしか使わないことを前提としている場合
#IF...Then...#Elseディレクティブ
でLongとLongLongを明示的に使い分ける場合
参考資料
Microsoft 公式文章
docs.microsoft.com - The Integer, Long, and Byte Data Types
しかし、最近のバージョンでは、Integer型として宣言されていても、VBAはすべての整数値をLong型に変換します。したがって、Integer変数を使用することによるパフォーマンス上の利点はなくなりました。実際、Long変数はVBAで変換する必要がないため、少し速くなる可能性があります。
以下のサイトでは、LongはIntegerの90%程度の処理時間だったようです。
説明が優しいので初心者にオススメです。
もりさんのプログラミング手帳 - 【ExcelVBA】Integer型 VS Long型、速いのはどっち?
以下のサイトは、何故Longのほうが早くなるのか、日本語で非常にわかりやすく解説されています。
VBA性能向上委員会 - Integer型とLong型はどちらを使うべき?
32bitのCPUの場合、処理単位が32bit単位で行われます。
そのため、16bitのInteger型は、一度32bitに変換してから処理を行います。
このPC内部での変換により、32bit領域のLong型の方が、結果的に速くなったというわけです。
不思議なことにこちらの記事は2018/9/9に調査された結果にもかかわらず、Integer>=Long
という結果になっています。
肝心の環境やバージョンを明記されていないので非常に気になります。
Excel作業をVBAで効率化 - VBAの数値型で一番速いのはどれか
まとめ
二種類の計測結果を見て分かるように、ちょっと別のコードが実行されたら埋もれてしまう程度の差しかありませんが、確かに64bit Office環境下ではLongLong
を使ったほうが高速なようです。
どうしてもパフォーマンスを優先したい場合は
- メモリに余裕がある
- Office 2007以前は動作対象に含めない
- この変数はLongでもLongLongでもどっちでも良い
という条件の元、LongPtrにしても良さそうです。
無闇矢鱈にLongPtrにすると、思わぬところでトラブルがおこるかもしれませんし、環境によって仕様が変わることになります。
既存のプログラムは、特別な理由がない限りはLongから変えないほうが良いのではないでしょうか。
私は「ここぞ」という時にだけLongPtrを使って行こうと思います。
以上
何か御座いましたらコメント欄、またはTwitterからどうぞ♪
それではまた来週♪ ちゅんちゅん(・8・)