プロが教える店舗&オフィスのセキュリティ対策術

みなさん、VBAの2次元配列への代入をループさせる時に、1次元・2次元どちらを外側のループにしますか?

というのも、下記サイトで配列へのアクセス高速化の方法(C言語です)が記載されており、興味を持ったので参考までにVBAで試してみたのですが、どうも結果が反対になるようなのです・・・

http://fast-programming.aglk.net/double-array/sa …

こちらのサイト内では、1次元を外側のループにしたほうが早い、という処理結果だと思いますが、
手元では反対になりました・・・

質問としては、
言語が異なると、配列へのアクセスもかわるのでしょうか?
それとも、私の記述がサイトのものと違っているのでしょうか?
下記内容も含め、気軽に回答していただけたら幸いです。

以下、ソースと結果です。
同じ内容で、①、②の部分だけ入れ替えて計測しました。
結果はコードのさらに下です。
'-------------------------------------------
Option Explicit
Declare Function GetTickCount Lib "kernel32" () As Long
Sub speedtest()
Dim i As Long, j As Long, cnt As Long
Dim st As Double, ft As Double
Dim table(1 To 1000, 1 To 1000)
Dim res As String
res = ""
For cnt = 1 To 10
st = GetTickCount()
For i = 1 To 1000
For j = 1 To 1000
table(i, j) = 1 ’・・・・①
' table(j, i) = 1 ’・・・・②
Next
Next
ft = GetTickCount() - st
res = res & cnt & " : " & ft & vbCrLf
Next
Debug.Print res
Erase table
End Sub
'-------------------------------------------
結果
<①>
1 : 62
2 : 63
3 : 78
4 : 62
5 : 63
6 : 78
7 : 62
8 : 62
9 : 63
10 : 78

<②>
1 : 31
2 : 47
3 : 31
4 : 31
5 : 31
6 : 47
7 : 31
8 : 31
9 : 32
10 : 46

A 回答 (3件)

後出しで回答はほぼNo2と同じですが、言語仕様によってアクセスではなく(アクセスかな?)メモリの確保の仕方が異なります。



参考に、メモリをどう確保しているかを確認する手段の1つを。

値をただ取り出すだけならForEachがダントツで速いんですが、順番が決まっていたり
値をセットしていく場合はループをどう組むかは考える必要があります。

どのようにメモリを確保しているかが分かれば、出来るだけアドレスの移動が少ない順になるよう
組めば良し、確認方法が分かれば言語が変わっても最適なループロジックを組めるように
なるかもしれません。

Cではポインタが使えるので配列がどういう順でメモリに展開されているかがわかるかと思いますが、
VBAの場合、ポインタが一応使えません。

が、簡単な確認方法だけ以下に提示します。
' 64bit
Private Declare PtrSafe Function VarPtrArray Lib "VBE7" Alias "VarPtr" (ByRef Var() As Any) As LongPtr
' 32bit(確認してない)
Private Declare Function VarPtrArray Lib "VBE7" Alias "VarPtr" (ByRef Var() As Any) As LongPtr

配列の宣言した後なり、値入れた後なりに以下。
Debug.Print Hex(VarPtr(table(0, 0)))
Debug.Print Hex(VarPtr(table(1, 0)))
Debug.Print Hex(VarPtr(table(2, 0)))
Debug.Print Hex(VarPtr(table(3, 0)))

Debug.Print Hex(VarPtr(table(0, 0)))
Debug.Print Hex(VarPtr(table(0, 1)))
Debug.Print Hex(VarPtr(table(0, 2)))
Debug.Print Hex(VarPtr(table(0, 3)))

VarPtrは変数のアドレスを返す関数です。それを16進数にして表示
質問内容のコードでは、配列tableでは型を指定していないので分かりやすく4バイトのLongにすると
 Dim table(1 to 1000, 1 to 1000) as Long
恐らく下記のような出力がされます。(Excelの起動し直しでアドレスは変わるが)
28B0040 ' table(1, 1)
28B0044 ' table(2, 1) 4byte増えてる
28B0048
28B004C

28B0040 ' table(1, 1)
28B0FE0 ' table(1, 2) 4000byte(0FA0)増えてる
28B1F80
28B2F20

Cと同じメモリ確保の仕方の言語が圧倒的だったり、Cから始めた人も多いせいか、
私もよく、arry[外,内]というようなループを作ってしまいます。
配列として確保し、配列として操作することが少なかったり、そこまで大きな配列を作らなかったりで
全然気にしない事も多いのですがこの質問で見直すべきだな、と改めて考えさせられました。
    • good
    • 2
この回答へのお礼

解り易い回答ありがとうございます。

こんな方法があるとは・・・
素人の私にとって、ただただ自分の無知を嘆くばかりです。

納得しました。

お礼日時:2015/03/13 12:27

vba 多次元配列 メモリ配置


で検索したところ、いくつか見付かりましたが、いずれも
A(1,1) の「となり」はA(2,1) だというものです。


多次元配列の考え方は、言語や、(同じ言語での)使用ライブラリ等によって異ります。
VBAではA(X,Y,Z) という考え
C では A[Z][Y][X] という考えを採用した、と言えるでしょう。
    • good
    • 1
この回答へのお礼

回答ありがとうございます。
>A(1,1) の「となり」はA(2,1) だというものです。

>VBAではA(X,Y,Z) という考え
C では A[Z][Y][X] という考えを採用した、と言えるでしょう。

うーん、なるほど。
私はどうも、二次元配列をワークシートでイメージしており、
メモリの配置の話がちんぷんかんぷんでしたが、今回の件は良い勉強になりました。

お礼日時:2015/03/13 12:19

一応確認だけど, カテゴリが Perl になってるのは認識してる?



さておき, そんなものは言語に依存する話だから「C でこうなっているから VBA でも同じだろう」などと安直に考えてはいけない.

もちろん「同じ言語ならどの処理系でも同じ」と言っていいかどうかも難しいところだ.
    • good
    • 1
この回答へのお礼

回答ありがとうございます。

>一応確認だけど, カテゴリが Perl になってるのは認識してる?

してません。カテゴリの存在を知りませんでした。

>さておき, そんなものは言語に依存する話だから「C でこうなっているから VBA でも同じだろう」などと安直に考えてはいけない.

その根拠を教えていただけまさんか?

>もちろん「同じ言語ならどの処理系でも同じ」と言っていいかどうかも難しいところだ.

そうですよね。

お礼日時:2015/03/12 19:16

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!