2016-06-19 30 views
14

我在.Net Nativestructs中發現了(可能會發生)過度優化的問題。我不確定編譯器是否過於激進,或者我太盲目,看不到我做錯了什麼。這是.Net本地編譯和優化中的一個可能的錯誤嗎?

要重現此,請按照下列步驟操作:

步驟1:創建一個新的空白通用(win10)在的Visual Studio 2015年更新2應用定位打造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:運行應用程序中的Debugx64。你應該可以看到這個標籤:

42.5,42.5 => 107.5,107.5

步驟5:在Releasex64運行應用程序。你應該可以看到這個標籤:

-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的構造函數之前創建了計算的本地副本。在調試模式下,這兩個功能完全相同。然而,在發行版中,某些內容正在得到優化。

這是我們的應用程序中的一個討厭的錯誤。你在這裏看到的代碼是從一個更大的圖書館中剝離出來的,恐怕還會有更多。在我向微軟報告這件事之前,如果你能看一看並且讓我知道爲什麼Inflate1在發佈模式下不起作用,我將不勝感激。爲什麼我們必須創建本地副本?

+5

結構的一大優點是使用它們的代碼總是可以進行大量優化。最大的問題在於它們歷來是優化器錯誤的頭號來源。只需發送報告。 –

+0

感謝@HansPassant,我剛剛給微軟發送了一封電子郵件。 – Laith

+1

有趣的是,如果將'30行'上的'foreach'循環更改爲'for'循環,它也會修復問題。奇怪的。 – Laith

回答

4

我很不清楚爲什麼這個問題有一個賞金。是的,這是@Matt告訴你的錯誤。他知道,他在.NET Native上工作。他記錄了臨時解決方法,使用一個屬性來阻止優化器對內聯方法進行內聯。這個技巧經常可以解決優化器錯誤。

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

他們會得到修復,下一個主要版本是通常的承諾。