先日、以下の記事を書いた。
thom.hateblo.jp
そのとき私は、VBAでメモリ番地の入れ替えはマシン語でも呼び出さないかぎり出来ないと思っていたのだが、ふつうにCopyMemoryで出来たようだ。
mmYYmmddさんがそれを実現する関数を作成してくれた。
https://mmyymmdd.hatenablog.com/entry/2015/08/05/235508mmyymmdd.hatenablog.com
さて、本当にByRef渡しするだけで番地入れ変えできているか、やはり自分で試さないと納得いかない性分なので、実際にやってみた。
'>|mmYYmmddさんのコードを引用| Public Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _ (ByRef Destination As Any, ByRef Source As Any, ByVal Length As LongPtr) ' Variant 変数のスワップ Sub swapVariant2(ByRef x As Variant, ByRef y As Variant) Dim tmp As Variant CopyMemory tmp, x, 16 CopyMemory x, y, 16 CopyMemory y, tmp, 16 CopyMemory tmp, Empty, 16 ' これが必要 End Sub '|引用ここまで|< Sub swapNormal(ByRef x, ByRef y) Dim tmp As Variant tmp = x x = y y = x End Sub Sub test() a = String(100000000, "★") b = String(100000000, "☆") Debug.Print "Take1:DataSwap" StartTime = Timer() For i = 1 To 10 Call swapNormal(a, b) Next i Debug.Print Left(a, 1) Debug.Print Round(Timer() - StartTime, 7) Debug.Print "Take2:AddressSwap" StartTime = Timer() For i = 1 To 10 Call swapVariant2(a, b) Next i Debug.Print Left(a, 1) Debug.Print Round(Timer() - StartTime, 7) End Sub
testプロシージャの動作は、1億個の星が入った変数2個の中身を相互に入れ替えるというもの。
それを10回繰り返す処理である。
実行してみたところ、次の結果になった。
Take1:DataSwap ☆ 5.957031 Take2:AddressSwap ☆ 0.0019531
これは凄い!
データを直接入れ替えた場合は約6秒かかっている。
メモリアドレスの入れ替えで対応するswapVariant2関数では、約0.002秒で終わった。
検証結果
アドレス交換を利用した変数のSwapは、普通にデータを交換するより3000倍速い。
完成したswapVariant2関数を使う分には問題ないと思うが、CopyMemoryを単体使用するのは知識が無いと危ないとのことなので注意。
8/9追記
CopyMemory関数に渡すLength引数について、Officeの64bit版では24になるとのこと。
恐らく、ディレクティブで判定する必要があるので注意。