次の方法で共有

エクセルVBAの実行処理速度がマウスカーソルを置く位置とfor-nextカウンターの初期値に依存する

Anonymous
2013-02-01T01:10:52+00:00

現在、日常的に使用しているexcelで処理時間が問題となり調査している過程で表題に挙げた問題に出会いました。

現象は以下の手順で容易に再現できるが原因がわかりません。よろしくお願いします。

簡単にその事象が確認出来るスクリプトを下記しました。

このスクリプト実行すると各サブ・プロシージャ(Proc_A,Proc_B)と全体の実行時間が表示される。

下の表は、このスクリプトを使って各々10回の試行にて測定した処理時間の平均とばらつきです。

実際にはPC環境を(DELL、Win7、Office2010-32Bit)、(Lenovo、Win7、Office2010-32Bit)、(Dynabook、Win8、Office2010-64Bit)

と変えて実施しました。処理時間は環境による違いはありますが、現象は変わりません。 下の表はDELL環境による測定結果です。

1.

Proc_A,Proc_Bのfor-nextカウンターJを(J=0 to 6)とし、マウスカーソル

  をシート内に置いた状態にし、ショートカット・キーでスクリプトを実行。

--------------- J=0, シート内 --+-- J=0, シート外 --

                         平均    分散       平均    分散

Proc_A(T1-T0)   0.002   0.000    0.002   0.000

Proc_A(T2-T1)   0.001   0.000    0.000   0.000

Proc_A(End-T0) 0.067   0.000    0.070   0.000

2.

Proc_A,Proc_Bのfor-nextカウンターJを(J=6 to 6)とし、マウスカーソル

  をシートの外に移した状態にし、ショートカット・キーでスクリプトを実行。

--------------- J=6, シート内 --+-- J=6, シート外 --

                         平均    分散       平均    分散

Proc_A(T1-T0)   0.000   0.000    0.000   0.000

Proc_A(T2-T1)   0.000   0.000    0.000   0.000

Proc_A(End-T0) 0.565   0.000    0.076   0.000

<------------ 今回のテストに使ったスクリプト ----->

Option Explicit

    Dim C As Variant

    Dim R As Variant

    Dim T0 As Double

    Dim T1 As Double

    Dim T2 As Double

Private Sub Proc_A()

    Dim i As Integer

    Dim j As Integer

    ReDim C(6, 300)

    For i = 0 To 299

        For j = 0 To 6

            C(j, i) = "Test"

        Next j

    Next i

End Sub

Private Sub Proc_B()

    Dim i As Integer

    Dim j As Integer

    ReDim R(6, 300)

    For i = 0 To 200

        For j = 0 To 6

            R(j, i) = C(j, i)

        Next j

    Next i

End Sub

Private Sub Proc_C()

    Dim i As Integer

    Dim j As Integer

    For i = 0 To 200

        For j = 0 To 6

            Cells(2 + i, 4 + j).Value = C(j, i)

        Next j

    Next i

End Sub

Sub Test()

    Application.ScreenUpdating = False

    Worksheets("sheet1").Activate

    T0 = CDbl(Timer)

    Range("B2:B5").ClearContents

    Range("D1:J300").ClearContents

    Proc_A                                  '配列Cに文字列"TEST"を代入。

    T1 = CDbl(Timer)

    Proc_B                                  '配列Cの各要素を配列Rに代入する。

    T2 = CDbl(Timer)

    Proc_C                                  '配列Rの各要素をセルに書き出す。

    Application.ScreenUpdating = True

    Range("B2").Value = T1 - T0             'Proc_Aの処理時間。

    Range("B3").Value = T2 - T1             'Proc_Bの処理時間。

    Range("B5").Value = CDbl(Timer) - T0    '全体の処理時間。

    Beep

End Sub

Microsoft 365 と Office | Excel | 家庭向け | Windows

ロックされた質問。 この質問は、Microsoft サポート コミュニティから移行されました。 役に立つかどうかに投票することはできますが、コメントの追加、質問への返信やフォローはできません。

0 件のコメント コメントはありません

質問作成者が受け入れた回答

Anonymous
2013-02-03T05:58:51+00:00

>0でのクリアやられましたか?

variantに戻して再度実行してみました。

確かに0で初期化するとマウスの内・外に関係なく約51mSで終了する事が確認できた。

その意味で、この「0クリア」は一つの解決法と言えると思います。

うむ、それでもどこかスッキリしない。 とりあえず、

今回の問題を以下に整理してみた。(私の環境での測定結果:再掲)

  1. J=0でスクリプトを実行すると、マウスの内・外に関係なくほぼ瞬時(50~70mS)で終了。
  2. J=6でスクリプトを実行すると、マウスの内・外に関係有り。外:瞬時(50~70mS)、内:560mS前後。(補足: J =1~6まで順に増すと約80~100mSづつ増加する)
  3. スクリプトの実行条件: エクセルシートのウインドウが最前面である事。(補足)

今回、頂いたコメントから解った事。

  1. 対処方法としてvariant配列をNull以外で初期化すれば回避出来る。

で、仮に初期化の無い事、或は、値の入ってない配列要素をセルに書き出す事、が問題だとすると、

依然、残される疑問として

  1. 初期化してない(ReDimのみ)の場合であっても、マウスが外の時はJ の値に関係なく瞬時に終了。  (問題整理の1.、2.)

と言う事実に対する説明が困難になる。

実際のアプリケーションで値の入ってない配列要素をセルに書き出すケースは殆ど無いと思います。

今回のスクリプトは質問投稿にあたり現象を簡単に再現出来る様にと作った特別なケースですが、

現象は決して特別とは思っていません。

この回答は役に立ちましたか?

1 人がこの回答が役に立ったと思いました。
0 件のコメント コメントはありません

11 件の追加の回答

並べ替え方法: 最も役に立つ
  1. Anonymous
    2013-02-01T13:09:53+00:00

    ヴァリアント変数は使っていません。

    ご指摘をいただいてから文字列型に変更しました。でも結果は同じです。

    >マウスの件は忘れてください。

    思い込みだと思いますよ。

    思い込みではありません。

    実際にスクリプトを実行されましたか?

    現実にタイマーが10倍も違うのをどの様に説明されますか?

    コードだけを見ただけではとても考えられない事だから質問しているのです。

    この回答は役に立ちましたか?

    2 人がこの回答が役に立ったと思いました。
    0 件のコメント コメントはありません
  2. Anonymous
    2013-02-12T08:15:27+00:00

    y sakuda さん、いつもアドバイスありがとうございます。

    Lazy Fox さん、こんにちは。

    マイクロソフト コミュニティをご利用いただきありがとうございます。

    紹介されているコメントが参考としていただけたようでよかったです。

    同様の質問でこちらのスレッドを見られた方にも確認として参考になるよう [ 回答としてマーク ] をさせていただきました。

    また質問や不明点がありましたらマイクロソフト コミュニティをぜひご利用ください。

    この回答は役に立ちましたか?

    1 人がこの回答が役に立ったと思いました。
    0 件のコメント コメントはありません
  3. Anonymous
    2013-02-01T10:58:41+00:00

    プロシージャのつくり方の基本が未だ呑み込めていません。

    紛れを残さない記述やグローバル変数の扱いなどとっても貴重なご意見

    ありがとうございました。

    >マウスカーソルを置く位置は関係ないでしょう。

    (まさかと思いましたが、結果に違いはありません)

    variant型配列を整数型のLongにかえて同様なテスト行ってみました。

    確かにこのケースではマウスカーソルの位置には関係しなようです。

    しかし、文字列型の配列にして同様なテストをすると質問時と同じ結果になります。

    つまり、J =6 to 6 としてマウスカーソルをシート内の任意のセルに置いておくと約560mSかかります。

    一方、同じ条件でマウスカーソルをシートの外に出すと約54mSで終わります。

    しかも、周りにあるどのPCでテストしても時間の差はありますが、J = 6 to 6 (初期値が0以外でも可)の

    時に限り、マウスカーソルがシートの内か外によって大きく差が出ます。

    J =0 to 6の時はマウスカーソルの位置は関係ありませんでした。

    よろしくお願いします。

    この回答は役に立ちましたか?

    0 件のコメント コメントはありません
  4. Anonymous
    2013-02-01T02:59:37+00:00

    マウスカーソルを置く位置は関係ないでしょう。

    (まさかと思いましたが、結果に違いはありません)

    問題はカウンタの扱いのようです。

    問題はProc_Cではどちらのケースもj=0 to 6 でやっておられるので、a,bをj=6 to 6にした場合、

    値をセットしていない状態のバリアント配列から大量にシートへの転記が発生しています。

    正直こういうことになるとは知りませんでしたので、ここから先は推測です。

    1.バリアント型は値が実際に設定されたところで、初めて型が決まる性質があります。

    2.シートに転記する際、セル側はセルにセットするデータの方を認識する必要がある

    3.値がセットされたバリアント変数については型は既にきまっているため、瞬時に転記されるが、

     値がセットされていない場合はその度に転記されるデータの型を決定しようとするため時間がかかる

    こんなところだと思います。

    参考まで。

    で、話は全く関係ありませんが、提示されたコードで気になるところを2~3述べさせていただきます。

    1.シートの指定がされていない

       Range("B2").Value = T1 - T0            

      とするとActiveSheetが対象になりますが、複数のシートを扱っている場合バグの原因になることが非常   に多いと考えます。

      With ActiveSheet

        .Range("B5").Value = CDbl(Timer) - T0   

      End with

      のように常にオブジェクトを完全に修飾して紛れをなくすべきです。

    2.グローバル変数の使用

     各プロシージャで共通に使用される変数をすべてグローバル変数として定義されています。

     確かにグローバル変数は便利な場合もあるのですが、全処理が終了しても値が初期化されないという厄介な問題があります。

     ですから1回目の処理とそれ以降の処理では初期状態が異なってしまい結果が違ってくる場合があります。

     また、トラブルが発生した際、その変数がどこで変更されているのか突き止めるのが非常に困難になる場合もあります。

     サブプロシージャへ渡す値や変数はパラメータ化することをお勧めします。

     ↓の例のように配列も渡せます。

    Sub aaa(pV())

      MsgBox UBound(pV)

      ReDim pV(100)

      MsgBox UBound(pV)

    End Sub

    Sub TestMain()

    Dim wAry()

    ReDim wAry(20)

    aaa wAry

    MsgBox UBound(wAry)

    End Sub

    この回答は役に立ちましたか?

    0 件のコメント コメントはありません