none
WithEventsってパフォーマンス的に問題無いのですか? RRS feed

  • 質問

  • お世話になります、いつもこちらのフォーラムにはお世話になっております(ROMってばっかりですが・・・)

    現在以下の環境にてシステム作成をしております
    OS WindowsXP
    VisualStudio2008 .NET 2.0  VisualBasic

    今まで曖昧だった所を整理したくご教授下さい。

    ButtonやTextBox等、様々なコントロールのイベントに関してですが

    WithEventsで処理させる場合と(ドラッグ&ドロップではこちらが自動生成されますよね)
    各イベント毎にAddHandlerをする場合する場合では内部的な
    処理としてかなり違うのでしょうか?

    WithEvent宣言されたコントロールは Handlesでプロシージャと
    紐付け出来るので、とても便利なのですが、その反面
    パフォーマンスが犠牲になっているのではないかと思っております。
    もしかして別の「利点」があるのでしょうか?


    利点があったとしても、パフォーマンス的に問題があるならば、
    画面に多数のコントロールを設置している場合はWithEventを使用しない
    ことも検討しなければいけないのではないか?
    と思っています。


    たとえば以下のコントロールは Me._Button1 でもアクセス出来ますよね
     Friend WithEvents Button1 As System.Windows.Forms.Button

    これって既に「余計な」処理が加わっているように思うのですが・・・

    どうぞよろしくお願いします。

    2008年4月10日 8:07

回答

  •  but-masaru さんからの引用

    ボタンの上などにマウスを重ねるとハイライトされたりしますが、
    これは、Mouse_Enter等のイベントが発生しているのでは無いのでしょうか?

    開発者が新たにコードを書こうと、書くまいと、そのコントロールの中身で実装されているコードは実行されます。

     

     but-masaru さんからの引用

    つまり、WithEventsやAddHandler等を用いてButton.ClickやButton.Mouse_Enter等に対してプロシージャを書かないとしても
    空の処理でイベントは常に発生しているととらえるべきなのでしょうか?

    前述のようにコントロールが何か処理をしています。

    (その量はコントロールや送られてくるメッセージによって異なる)

    イベントを呼び出すタイミングで、1個も登録されていなければイベントを呼ぶ処理がスキップされるだけです。

     

    簡単なイメージ:

    マウスがコントロールの上を通過→Windowsからメッセージが送られてくる→コントロールがそのメッセージをハンドル→何らかの処理を行う→イベントが登録されていればイベントを呼び出す

    2008年4月10日 14:43
    モデレータ
  •  but-masaruさんおはようございます。

     暗黙的に空のイベントハンドラが呼ばれるとイメージされているのであればそうではなく、暗黙的にAddHandler,RemoveHandlerが呼ばれるのです。

     れいさんが書いておれる「SetではClass内のHandleキーワードに対応するため、Setされるオブジェクト、元のオブジェクトのイベントに対してAddHandler、RemoveHandlerを呼び出します。」をVBに直して書けばこんな感じです。

     

    Code Snippet

      Dim WithEvents Timer1 As Timer

      Dim _Timer2 As Timer


      Public Property Timer2() As Timer
        Get
          Return _Timer2
        End Get
        Set(ByVal value As Timer)
          If _Timer2 IsNot Nothing Then RemoveHandler Timer2.Tick, AddressOf Timer1_Tick
          _Timer2 = value
          If _Timer2 IsNot Nothing Then AddHandler Timer2.Tick, AddressOf Timer1_Tick
        End Set
      End Property

     

      Private Sub Test()
        For i As Integer = 0 To 100000
          Timer1 = New Timer()
        Next
        For i As Integer = 0 To 100000
          Timer2 = New Timer()
        Next
      End Sub

     

     

     

    2008年4月10日 23:23

すべての返信

  • 外池と申します。確証をもって詳しくご説明することはできないので、ご満足いただけないかもしれませんが・・・。(他の方のツッコミ大歓迎)

     

    私の理解では、まったく差はないものと考えています。

     

    WithEventsを用いてコントロールを宣言しHandlesを使ってイベントを記述する場合も、コンパイラーが生成するコードの中では、AddHandlerと同等の処理に展開されていると思います。AddHandlerというイベントを処理するメソッドを動的に割り当てる文と、Handlesという静的な割り当ての方法の両方を備えていることは、VBの言語としての特徴であって、.Net Frameworkとしての機能は、前者の動的な方法のみだと思います・・・。

     

    ちなみに、C#には、AddHandlerに相当する動的な方法しかありません。

    -----

    以下は、禅問答のような感じなので、スルーしてもらって構いませんが・・・、参考までに。

     

    もし、パフォーマンスに差があったとしても、ユーザーが実際にマウスやキーボードを操作して発生するイベントの数は、パフォーマンスの観点で問題になるような量ではないと思います。ですので、イベントに割り当てられたメソッドを呼び出すところでのオーバーヘッドは無視していいんじゃないかと。

     

    それより、イベントに割り当てられたメソッドが重い処理だった場合には、大問題になり得ますが。

     

    まぁ・・・、イベントというものは、「(コンピューターの速さから比べて)時々発生する事象をとらえて、サクっと処理する」という感じのものだと思います。

     

    その点、人の操作で発生する事象は・・・、「時々」と考えて良いかと。

     

     

     

    2008年4月10日 8:25
  • 確証を持ちたい人は、IL で比較検証すればいいんじゃないでしょうか。


    # 私はやらないw

    2008年4月10日 9:03
  • WithEventsで定義されたフィールドは、Handleキーワードでのイベント処理ができるよう、

    VBによって少し変形されてコンパイルされます

     

    まず、WithEventsフィールドはVBによってプロパティに自動で変換されます。

    たとえば、

    WithEvents TextBox1 as TextBox

    の場合は、

    TextBox1というプロパティと、_TextBox1というフィールドが自動で作成されます。

     

    自動で定義されたGetはフィールドを返すだけですが、

    SetではClass内のHandleキーワードに対応するため、

    Setされるオブジェクト、元のオブジェクトのイベントに対してAddHandler、RemoveHandlerを呼び出します。

     

    違いは、これだけです。

    これによって、Handleキーワードが正確に動作します。

     

    このため、いくつか注意が必要です。

     

    ------------------

    ・マルチスレッド

    生成されるSet句はスレッドセーフには実装されていません。

    マルチスレッドでHandle句を使ったりするときには気をつけないといけません。

     

    ・パフォーマンス

    イベントの呼び出しはC#などと同じコストですが、

    AddHandler/RemoveHandlerの分、実際にはプロパティに変換されるフィールドに対するSetのコストが高くなります。

     

    WithEvents Timer1 As Timer

    Dim Timer2 As Timer

    Private Sub Timer1_Tick(xxx) Handles Timer1.Tick

     

    Private Sub Test

    for i=0 to 100000

    Timer1 = New Timer()

    next

    for i=0 to 100000

    Timer2 = New Timer()

    next

    End Sub

     

    みたいなコードで確認すると差は歴然としています。

     

    ・リフレクション

     

    WithEventsフィールドはILになるとプロパティになっていますので、

    リフレクションを使うときには型が変わります。

    ------------------

     

    この程度であろうと思います。

    WithEventsを使うときには、イベント発生のコストではなく、

    オブジェクトの割り当てのコストを考えねばいけないのに留意してください。

     

    GUIでドラッグドロップしている場合はオブジェクトを作り直したりしませんから、

    パフォーマンスはほとんど変わりません。

     

    2008年4月10日 9:08
  •  れい さんからの引用

    ・マルチスレッド

    生成されるSet句はスレッドセーフには実装されていません。

    マルチスレッドでHandle句を使ったりするときには気をつけないといけません。

    ダウト!!

    とはいってもまああまり良い実装でもないんですけどね。

    2008年4月10日 9:26
  •  なちゃ さんからの引用

    ダウト!!

    とはいってもまああまり良い実装でもないんですけどね。

     

    なんと!

     

    While True

    XXComponent1 = New XXComponent1

    End While

     

    みたいなコードをWorkerスレッドで実行しつつ、

    GUIスレッドでXXComponentにイベントを発生させてたら

    イベントが呼ばれなかったり、エラーをはいたりしたような。

     

    昔なので.Net1.0とか1.1かも知れません。

    今は治ってるということなのでしょうか?

     

    識者募集。

    #私もILを読む気はない。

    2008年4月10日 9:41
  • 外池様、じゃんぬねっと様、れい様、なちゃ様

    質問にお答え頂き、ありがとう御座います。

    なるほど、皆様のご意見を総合的にとらえると
    WithEventsにてイベントが指定されるのはそれほど
    気にすることでは無いと言うことですね(VBを使っている以上)

    私が思っていたのはWithEventsを使用すると暗黙的に
    内部で空っぽのイベントが発生してしまっているのでは
    無いか、、、という点でした。

    AddHandlerを指定した場合や、C#での += new ..
    などでは明示しているので、それ以外のイベントは発生
    しないのでは無いのか、、、とも思っていました。

    質問していて余計分からない部分が増えてしまったのですが
    たとえば作成しているプロジェクトのプロパティにある
    アプリーケーションタブにて「XP Visualスタイルを有効にする」
    のチェックを入れると視覚効果が有効になりますよね?

    ボタンの上などにマウスを重ねるとハイライトされたりしますが、
    これは、Mouse_Enter等のイベントが発生しているのでは無いので
    しょうか?

    私はこれらのイベントはコントロールにWithEventsが設定されて
    いるから自動的に起きていると思っていたのですが、再確認をし
    てみたところ、設定されていなくてもその現象が起きますね。

    つまり、WithEventsやAddHandler等を用いてButton.Clickや
    Button.Mouse_Enter等に対してプロシージャを書かないとしても
    空の処理でイベントは常に発生しているととらえるべきなので
    しょうか?

    コントロールが沢山貼り付けてあるWindowsForm上で
    マウスポインタをぐりぐり(表現が悪くてすみません)
    回していると、CPUの使用率がそのアプリケーションに
    対してあがりますよね、これはつまり上記の現象が起きている
    と理解するべきなのですか?


    以下の内容、確認してみました---------------------------------------

    IL DASMを使って実行ファイルをみたところ、WithEvent
    キーワードを宣言したコントロールは以下の内容が追加
    されている事を確認出来ました。

    Prop コントロール名: instance class......
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    確かにこの中にはSetとGetが記述されていました。

    VBでのAddHandler,C#での += new System.EventHandlerでは
    上記のProp...が生成されないのも確認出来ました。

    ILで見たときはC#の方がすっきりしていて気持ちいいですね、でも
    パフォーマンスに差は無いと言うことですか・・・うーむ


    WithEventsの 無し/有り に関しましては
    Forループで10万回づつ回したところアドバイス通りの結果になりました

    ※Timerオブジェクトにて

    Core2Duo L7300のPC     約  900ミリ秒 / 1200ミリ秒
    Celeron 2.4GHzのPC   約 1700ミリ秒 / 2450ミリ秒


    このことから、多少なりともWithEventsを指定することによって
    オーバーヘッドが生じていると理解しました。

    ----------------------------------------------------------------




    2008年4月10日 14:12
  • WithEventで生成したset_TextBox1とやらを見ると、メソッドの属性として"synchronized"がついてますね。

    これと同等の意味合いなら一応、lockで保護しているという風に見て良いのかな…。

    http://msdn2.microsoft.com/ja-jp/library/system.reflection.methodimplattributes.aspx


     

    ILを深く理解する気はないのですが、軽く流し読みする限り、AddHandlerでは毎回、対象のインスタンスを取得するためにget_TextBox1をやるので若干気にはなります。

    その点、set_TextBox1方式のHandlesではインスタンスはメンバー変数から取っているようなのでマシか?

     

    といっても、大差が出るほどではないように見えますが…。

    何度もやるわけじゃないですし。

    2008年4月10日 14:36
    モデレータ
  •  but-masaru さんからの引用

    ボタンの上などにマウスを重ねるとハイライトされたりしますが、
    これは、Mouse_Enter等のイベントが発生しているのでは無いのでしょうか?

    開発者が新たにコードを書こうと、書くまいと、そのコントロールの中身で実装されているコードは実行されます。

     

     but-masaru さんからの引用

    つまり、WithEventsやAddHandler等を用いてButton.ClickやButton.Mouse_Enter等に対してプロシージャを書かないとしても
    空の処理でイベントは常に発生しているととらえるべきなのでしょうか?

    前述のようにコントロールが何か処理をしています。

    (その量はコントロールや送られてくるメッセージによって異なる)

    イベントを呼び出すタイミングで、1個も登録されていなければイベントを呼ぶ処理がスキップされるだけです。

     

    簡単なイメージ:

    マウスがコントロールの上を通過→Windowsからメッセージが送られてくる→コントロールがそのメッセージをハンドル→何らかの処理を行う→イベントが登録されていればイベントを呼び出す

    2008年4月10日 14:43
    モデレータ
  •  but-masaruさんおはようございます。

     暗黙的に空のイベントハンドラが呼ばれるとイメージされているのであればそうではなく、暗黙的にAddHandler,RemoveHandlerが呼ばれるのです。

     れいさんが書いておれる「SetではClass内のHandleキーワードに対応するため、Setされるオブジェクト、元のオブジェクトのイベントに対してAddHandler、RemoveHandlerを呼び出します。」をVBに直して書けばこんな感じです。

     

    Code Snippet

      Dim WithEvents Timer1 As Timer

      Dim _Timer2 As Timer


      Public Property Timer2() As Timer
        Get
          Return _Timer2
        End Get
        Set(ByVal value As Timer)
          If _Timer2 IsNot Nothing Then RemoveHandler Timer2.Tick, AddressOf Timer1_Tick
          _Timer2 = value
          If _Timer2 IsNot Nothing Then AddHandler Timer2.Tick, AddressOf Timer1_Tick
        End Set
      End Property

     

      Private Sub Test()
        For i As Integer = 0 To 100000
          Timer1 = New Timer()
        Next
        For i As Integer = 0 To 100000
          Timer2 = New Timer()
        Next
      End Sub

     

     

     

    2008年4月10日 23:23
  •  Azulean さんからの引用

    簡単なイメージ:

    マウスがコントロールの上を通過→Windowsからメッセージが送られてくる→コントロールがそのメッセージをハンドル→何らかの処理を行う→イベントが登録されていればイベントを呼び出す



    イベントの件、理解しました。

    「あたりまえすぎる」事を聞いてしまったみたいですね。
    Form上のキャプションバーをクリックして移動出来るのも、
    Formコントロールが元々持っているイベントに「移動する処理」が
    実装されているからですものね。 

    ありがとう御座います。

    2008年4月11日 2:50
  •  Azulean さんからの引用

    ILを深く理解する気はないのですが、軽く流し読みする限り、AddHandlerでは毎回、対象のインスタンスを取得するためにget_TextBox1をやるので若干気にはなります。

    その点、set_TextBox1方式のHandlesではインスタンスはメンバー変数から取っているようなのでマシか?



    この件、私のつたない読解力でILを見てみたのですが、WithEventsを使わないコントロールに
    AddHandlerにてイベントを実装した場合は get_...というメソッドは生成されてませんでした

    WithEvents にてイベントを実装した場合は以下の内容が生成されていました
    Method get_...
    Method set_...
    Prop .......

    見るところ間違えているかな・・?

    メソッドの属性って、ILで確認出来るんですね!

    れい様がおっしゃっていた 「Set句がスレッドセーフでは無い」というのはILをみれば分かることだったんですね

    instance void set_[コントロール](ほにゃらら) cil managed synchronized 

    Azulean様のアドバイスでどの様に見るのかやっと分かりました!(み、見方が分かっただけですけど・・)

    うーむ、いろいろ理解するには時間が必要ですねぇ

    また一つ勉強になりました、ありがとう御座います
    2008年4月11日 3:16
  •  三輪の牛 さんからの引用

     but-masaruさんおはようございます。

     暗黙的に空のイベントハンドラが呼ばれるとイメージされているのであればそうではなく、暗黙的にAddHandler,RemoveHandlerが呼ばれるのです。



    具体的な提示をして下さり、ありがとう御座います。

    件名になっていた「パフォーマンス」に関しては、イベントの挙動ではなく
    WithEventsによって暗黙に置き換わったget,set(特にset)の挙動、
    つまりProperty Methodのパフォーマンスがどうなのか?
    という話になるわけですよね

    このあたりがちゃんと分かっただけでもスッキリしました。
    (皆さんはじめからそー言っていますよね;;)

    C#には存在しないというのはもともとVB独自のキーワードだったからなのですね。
    こういう部分も知りませんでした(大枠は両方同じだと思ってましたので)

    get,setが生成されるのが「いやだー」な場合はWIthEventsを削除すれば良いわけで、、、
    (でもWithEventsがとられたコントロールをダブルクリックすると・・・やっぱり Handles生成!
    開発環境はWithEventsを使う前提で動くようになっているということも分かりました。)

    WithEventsを宣言することによってHandlesが利用できる点に関しては、
    確かに視覚的にわかりやすいですね!
    この点は重宝しています(私がC#乗り換えに踏み込めない1つ目の理由)


    ありがとう御座いました、この件は解決にしたいと思います。

    これからも勉強させて頂きます(^^
    2008年4月11日 4:15