4

私はいくつかの長方形があり、それぞれにホバー効果を持たせたいプロジェクトに取り組んでいます。これで、 WM_MOUSEMOVEメッセージをキャプチャして、各長方形を反復処理できることがわかりました。しかし、長方形がたくさんある場合(50がたくさんある場合)はどうなりますか。
私は間違っているかもしれませんが、マウスが動くたびにその数を繰り返してテストを実行し、アプリケーションの速度を少し遅くしませんか?

それから、オペレーティングシステム(Windowsなど)がこれをどのように行うのか疑問に思い始めました。現在、画面上に100以上のものがあり、それらにカーソルを合わせると、すべてに何らかのアニメーションが表示されます。また、マウスがピクセルを移動するたびにウィンドウがすべてを繰り返すとは思いません。

基本的に:
1。パフォーマンスを考慮して、約50個の長方形がある場合、マウスがどの長方形の上にあるかをどのように把握できますか。
2. Windowsはこれをどのように行いますか?(私は何よりも興味がありますが、複雑でなければ、自分のプログラムに似たようなものを実装できるでしょうか?)

ああ、それらはすべて長方形であり、回転することも何もありません。

4

5 に答える 5

7

コードの一部が実際のボトルネックを作成していることが明らかになるまで、私はパフォーマンスについてあまり気にしません。このようなボトルネックがあると仮定して、次のコードのパフォーマンスを測定してみましょう (コードは C# ですが、C++ が遅くなることはないと確信しています)。

public class Rectangle
{
    public int X { get; set; }
    public int Y { get; set; }
    public int W { get; set; }
    public int H { get; set; }

    public bool HitTest(int x, int y)
    {
        return x >= X && x < X + W && y >= Y && y < Y + H ? true : false;
    }
}

メソッドのパフォーマンスに興味があるHitTest()ので、測定してみましょう。

void PerformanceTest()
{
    const int Iterations = 1000000;
    Random rnd = new Random();
    var rectangles = Enumerable.Range(1, 50).Select(
            r => new Rectangle {
                X = rnd.Next(1000),
                Y = rnd.Next(1000),
                W = rnd.Next(1000),
                H = rnd.Next(1000)}).ToList();

    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (int i = 0; i < Iterations; i++)
    {
        rectangles.ForEach(r => r.HitTest(500, 500));
    }
    sw.Stop();

    Console.WriteLine("Elapsed time: {0}ms. ({1}us per one iteration)",
        sw.ElapsedMilliseconds,
        (float)sw.ElapsedMilliseconds * 1000 / Iterations);
}

私のPCでは、上記のコードは次のように出力されます:

経過時間: 701ms。(1 回あたり 0.701us)

ご覧のとおり、 50 個の長方形のヒット テストにかかる時間は1 マイクロ秒未満です。これは、派手なホバー効果を作成するのにかかる時間や、プログラムが行うその他の処理に比べて長すぎると本当に思いますか? もちろん、この質問に答えられるのはあなただけです。

しかし、私の話の教訓は次のとおりです。事前に最適化しようとしないでください。また、まったく存在しない可能性のある問題を解決しようとして時間を費やしてはなりません。

于 2012-03-02T15:23:10.937 に答える
1

ここでの他の質問はあなたのパート2に答えなかったので、私はそれを試してみます:

2. Windowsはこれをどのように行いますか?(私は何よりも興味がありますが、複雑でなければ、自分のプログラムに似たようなものを実装できるでしょうか?)

何十ものウィンドウを開いていても、それぞれに多くのツールバーがあり、それぞれに多くの項目があるなど、マウスを動かすたびに、ウィンドウがすべてをチェックする必要がないことを理解してください。

Windowsは基本的に2つの層で構成されています。HWNDがあります。これは、Windows自体がデスクトップ上のスペースの細分化を管理する方法です。通常、各HWND内には、そのHWND内の独自のスペースを管理するコントロールがあります。独自のリストアイテムを管理するリストボックス、独自のタブを管理するタブコントロール、独自のHTMLページレイアウトを管理するHTMLコントロールなどです。(または、あなたの場合、50個ほどの長方形を管理するコード。)

マウスが移動すると、Windowsは最初にそのWM_MOUSEMOVEを送信する正しいHWNDを決定します。そしてそれはHWNDを横断することによってそうします。HWNDは、包含を表すツリー、およびZ-Orderを表す兄弟間の順序として格納されるため、Windowsは、このツリーに単純な深さ優先探索を実行して、任意の時点で最下部のHWNDを見つけることができます。Spy ++アプリを起動すると、このHWNDツリーがどのように見えるかを自分で確認できます。Windowsは完全なトラバーサルを行っていないことに注意してください。たとえば、トップレベルのアプリケーションウィンドウをトラバースしているときに、ポイントが含まれている最初のトップレベルHWNDを見つけるとすぐに、ポイントが含まれているアプリを見つけます。その下/後ろにある他のすべてのアプリとその中のすべてのコントロールを完全に無視して、それを掘り下げます。

Windowsが正しいHWNDを決定すると、それに適切なメッセージ(WM_NCHITTEST、WM_MOUSEMOVEなど)を送信します。その後、Windowsは、自身のコンテンツに対して同様に行うのはそのコントロール次第です。固定サイズのアイテムを含むリストボックスの場合、特定のポイントでのアイテムの決定は、除算操作と同じくらい簡単な場合があります。または、HTMLコントロールの場合、コントロールには「レイアウトツリー」に相当する独自の機能があり、その時点で要素をすばやく判別するために使用できます。あなたの場合、長方形のリストをループすることは完全にうまくいくかもしれません。

これはやや単純化されたバージョンです。上記よりも少し複雑です。たとえば、ウィンドウは、ポイントインライトチェックだけでなく、奇妙な形の透明なウィンドウ(および非表示のウィンドウと無効なウィンドウ)を許可する他のチェックがあります。しかし、基本的な樹木降下の考え方が当てはまります。

覚えておくべきもう1つの重要な問題は、これはすべて非常に速いことです。マウスの移動は「人間の時間」で行われ、最新のCPUは、マウスが数ピクセル移動するのにかかる時間内に多くの操作を実行できます。画面。最後に、画面上のポイントAからポイントBにマウスを移動すると、特にマウスをすばやく移動した場合、マウスがその間のすべてのピクセルを移動するとは限らないことに注意してください。

于 2012-03-04T02:49:45.940 に答える
1

パフォーマンスについて考えないでください。だったら測ってみよう!

マウス イベントは非常に低レベルのイベントで、非常に高速です。Windows はマウス メッセージをキューに入れ、アプリケーションはそれを読み取るか無視します。マウス イベント ハンドラーでは、どの四角形がマウスであるかを確認する操作は高速です。

あなたの「四角形」がウィンドウコントロールである場合(そしてそうあるべきです)、各コントロールにマウスリスナーを設定して、適切なハンドラーがウィンドウによって自動的に呼び出されるようにすることができます。

于 2012-03-02T14:07:27.333 に答える
0

答えには完全に不要な「ブランチ」が1つあると思います(テストでは、余分な100万回の操作が発生します)。「HitTest」では、次のように終了します。

return ... ? true : false;

式がすでに「true」または「false」であるため、「? true : false」は冗長です。超効率的なコードを作ろうとするとき、私はいつも実行される「操作」について考えます...

PS: また、++var と var++ のようなものは、コードでの使用方法に応じて、パフォーマンスに大きな影響を与える可能性があります (オプティマイザーがそれらのいくつかをキャッチして修正するため...

PPS: 私はそれに取り組んでいます... また、ループが式の結果を変更しない限り、ループに式またはメソッド呼び出しを入れないでください。 . これが 100 万回ループした場合、メソッドは 100 万回呼び出されます :)

于 2012-03-31T14:48:10.503 に答える
0

四角形の数が少ない場合 (たとえば 50 個)、それぞれを順番にテストするという明白なアプローチが最も速い可能性が高いことに同意します。

Windowsもほぼ同じことをしていると思います。明らかに、マウス ポインターが親ウィンドウにない限り、子ウィンドウをテストする必要はありません。また、最も設計の悪いダイアログでさえ、一度に 100 以上のコントロールが表示されることはめったにありません。多くのヒット テスト領域 (ListView、グリッドなど) を持つコントロールは、独自のヒット テストを最適化します。

何万もの四角形がある場合、パフォーマンスが問題になる可能性があり、ここで説明する方法のいずれかを使用できます。

于 2012-03-03T16:08:37.600 に答える