c# - 这是.Net Native 编译和优化中可能存在的错误吗?

标签 c# struct uwp compiler-optimization .net-native

我在 .Net Nativestructs 中发现了(可能是)过度优化的问题。我不确定是编译器太激进了,还是我太盲目了,看不出我做错了什么。

要重现这一点,请按照下列步骤操作:

第 1 步:在 Visual Studio 2015 Update 2 中创建一个新的空白通用 (win10) 应用,目标版本为 10586,最小版本为 10240。调用项目NativeBug 所以我们有相同的命名空间。

第 2 步:打开 MainPage.xaml 并插入此标签

<Page x:Class="NativeBug.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <!-- INSERT THIS LABEL -->
        <TextBlock x:Name="_Label" HorizontalAlignment="Center" VerticalAlignment="Center" />
    </Grid>
</Page>

第 3 步:将以下内容复制/粘贴到 MainPage.xaml.cs

using System;
using System.Collections.Generic;

namespace NativeBug
{
    public sealed partial class MainPage
    {
        public MainPage()
        {
            InitializeComponent();

            var startPoint = new Point2D(50, 50);
            var points = new[]
            {
                new Point2D(100, 100), 
                new Point2D(100, 50), 
                new Point2D(50, 100), 
            };

            var bounds = ComputeBounds(startPoint, points, 15);

            _Label.Text = $"{bounds.MinX} , {bounds.MinY}   =>   {bounds.MaxX} , {bounds.MaxY}";
        }

        private static Rectangle2D ComputeBounds(Point2D startPoint, IEnumerable<Point2D> points, double strokeThickness = 0)
        {
            var lastPoint = startPoint;
            var cumulativeBounds = new Rectangle2D();

            foreach (var point in points)
            {
                var bounds = ComputeBounds(lastPoint, point, strokeThickness);
                cumulativeBounds = cumulativeBounds.Union(bounds);
                lastPoint = point;
            }

            return cumulativeBounds;
        }

        private static Rectangle2D ComputeBounds(Point2D fromPoint, Point2D toPoint, double strokeThickness)
        {
            var bounds = new Rectangle2D(fromPoint.X, fromPoint.Y, toPoint.X, toPoint.Y);

            // ** Uncomment the line below to see the difference **
            //return strokeThickness <= 0 ? bounds : bounds.Inflate2(strokeThickness);

            return strokeThickness <= 0 ? bounds : bounds.Inflate1(strokeThickness);
        }
    }

    public struct Point2D
    {
        public readonly double X;
        public readonly double Y;

        public Point2D(double x, double y)
        {
            X = x;
            Y = y;
        }
    }

    public struct Rectangle2D
    {
        public readonly double MinX;
        public readonly double MinY;
        public readonly double MaxX;
        public readonly double MaxY;

        private bool IsEmpty => MinX == 0 && MinY == 0 && MaxX == 0 && MaxY == 0;

        public Rectangle2D(double x1, double y1, double x2, double y2)
        {
            MinX = Math.Min(x1, x2);
            MinY = Math.Min(y1, y2);
            MaxX = Math.Max(x1, x2);
            MaxY = Math.Max(y1, y2);
        }

        public Rectangle2D Union(Rectangle2D rectangle)
        {
            if (IsEmpty)
            {
                return rectangle;
            }

            var newMinX = Math.Min(MinX, rectangle.MinX);
            var newMinY = Math.Min(MinY, rectangle.MinY);
            var newMaxX = Math.Max(MaxX, rectangle.MaxX);
            var newMaxY = Math.Max(MaxY, rectangle.MaxY);

            return new Rectangle2D(newMinX, newMinY, newMaxX, newMaxY);
        }

        public Rectangle2D Inflate1(double value)
        {
            var halfValue = value * .5;

            return new Rectangle2D(MinX - halfValue, MinY - halfValue, MaxX + halfValue, MaxY + halfValue);
        }

        public Rectangle2D Inflate2(double value)
        {
            var halfValue = value * .5;
            var x1 = MinX - halfValue;
            var y1 = MinY - halfValue;
            var x2 = MaxX + halfValue;
            var y2 = MaxY + halfValue;

            return new Rectangle2D(x1, y1, x2, y2);
        }
    }
}

第 4 步:在Debug x64 中运行应用程序。你应该看到这个标签:

42.5 , 42.5 => 107.5 , 107.5

第 5 步:在 Release x64 中运行应用程序。你应该看到这个标签:

-7.5 , -7.5 => 7.5, 7.5

第 6 步:取消注释 MainPage.xaml.cs 中的 line 45 并重复第 5 步。现在您会看到原始标签

42.5 , 42.5 => 107.5 , 107.5


通过注释掉 line 45,代码将使用 Rectangle2D.Inflate2(...),这与 Rectangle2D.Inflate1(.. .) 除了它在将计算发送到 Rectangle2D 的构造函数之前创建计算的本地副本。在 Debug模式下,这两个功能完全一样。然而,在发布中,一些东西正在得到优化。

这是我们应用程序中的一个严重错误。您在此处看到的代码是从一个更大的库中删除的,恐怕还有更多。在我向 Microsoft 报告此问题之前,如果您能看一下并告诉我为什么 Inflate1 在 Release模式下不起作用,我将不胜感激。为什么我们必须创建本地副本?

最佳答案

我不太清楚为什么这个问题有赏金。是的,正如@Matt 告诉您的那样,这是一个错误。他知道,他从事 .NET Native 方面的工作。他记录了临时解决方法,使用属性来防止优化器内联该方法。一种经常用于绕过优化器错误的技巧。

using System.Runtime.CompilerServices;
....
    [MethodImpl(MethodImplOptions.NoInlining)]
    public Rectangle2D Inflate1(double value)
    {
        // etc...
    }

他们会修复它,下一个主要版本是通常的 promise 。

关于c# - 这是.Net Native 编译和优化中可能存在的错误吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37908160/

相关文章:

c# - 防止 FlowLayoutPanel 滚动到更新的控件

c# - 自行安装 WInService

C CUDA 从设备向主机发送结构数组

c# - 无法使用 UWP 应用程序访问我系统上的 Word 文档

c# - 在 UWP 中使用 Environment.SpecialFolder

xaml - UWP透明窗口

C#串口通信协议(protocol)

c# - 通过外部C#代码在游戏窗口中移动鼠标

c - 链表中的节点是单独的结构还是同一结构的一部分?

go - 在 golang 的另一个结构中重用结构