C#+WPFチューニング戦記

C#とWPFで高速なコードと最適なシステムを書くためにやってきたいろいろな事を書いてみます。.NET Frameworkのソースコードを読み解きましょう。なお、ここに書かれているのは個人の見解であって何らかの団体や企業の見解を代表するものではありません。

Rect.Containsもreadonly struct + in で高速化できる

RectとImmutableRectのContainsのベンチマークです。 IsEmptyの判定を排除できればもっと速度が出るのですが、互換性を重視してあえて削除していません。

なんでこれほどRectに拘るかといいますと、仮想化は矩形の判定の塊だからです。仮想化を高速化したいのに矩形の判定を横着するのは勿体無い、というわけです。

Method Mean Error StdDev
Rect_Rect_Contains 117.81 us 0.2385 us 0.1862 us
ImmutableRect_ImmutableRect_Contains 39.04 us 0.1703 us 0.1593 us
ImmutableRect_Rect_Contains 62.09 us 0.3684 us 0.3446 us

これが、今回の範囲に関係するソースコードです。

[Benchmark]
public void Rect_Rect_Contains()
{
    var rect1 = new Rect(0, 0, 5000, 5000);
    for (int idx = 0; idx < 10000; idx++)
    {
        _contains_results[idx] = rect1.Contains(new Rect(0, 0, idx, idx));
    }
}

[Benchmark]
public void ImmutableRect_ImmutableRect_Contains()
{
    var rect1 = new ImmutableRect(0, 0, 5000, 5000);
    for (int idx = 0; idx < 10000; idx++)
    {
        _contains_results[idx] = rect1.Contains(new ImmutableRect(0, 0, idx, idx));
    }
}

[Benchmark]
public void ImmutableRect_Rect_Contains()
{
    var rect1 = new ImmutableRect(0, 0, 5000, 5000);
    for (int idx = 0; idx < 10000; idx++)
    {
        _contains_results[idx] = rect1.Contains(new Rect(0, 0, idx, idx));
    }
}

public readonly struct ImmutableRect : IEquatable<ImmutableRect>
{
    public readonly double X;

    public readonly double Y;

    public readonly double Width;

    public readonly double Height;

    public ImmutableRect(double x, double y, double width, double height) => (X, Y, Width, Height) = (x, y, width, height);

    public static implicit operator ImmutableRect(Rect source) => new ImmutableRect(source.X, source.Y, source.Width, source.Height);

    public static implicit operator Rect(in ImmutableRect source) => new Rect(source.X, source.Y, source.Width, source.Height);

    // 2018/08/20 より高速なコードに更新
    public bool Contains(in ImmutableRect rect)
    {
            return Width >= 0 && Height >= 0 // !IsEmpty
                && X <= rect.X
                && Y <= rect.Y
                && X + Width >= rect.X + rect.Width
                && Y + Height >= rect.Y + rect.Height;
    }
}