2014-12-17 21 views
9

Tôi đã tìm thấy một hành vi khó chịu phát triển với Visual Studio. Nó đang treo máy của tôi trong khi biên dịch C#.Biên dịch tràn bộ nhớ C# compiler (csc.exe) biên soạn các kiểu lồng nhau và Linq

Tôi đã làm giảm hành vi vào mã tiếp theo tối thiểu nguồn

using System.Collections.Generic; 
using System.Linq; 

namespace memoryOverflowCsharpCompiler { 

    class SomeType { public decimal x; } 

    class TypeWrapper : Dictionary<int, 
         Dictionary<int, 
         Dictionary<int, SomeType [] []>>> { 

     public decimal minimumX() { 
      return base.Values.Min(a => 
         a.Values.Min(b => 
         b.Values.Min(c => 
         c  .Sum(d => 
         d  .Sum(e => e.x))))); 
     } 
    } 
} 

Biên soạn với

PROMPT> csc source.cs 

    *** BANG! overflow memory usage (up to ~3G) 

PROMPT> csc /? 
Microsoft (R) Visual C# Compiler version 12.0.30501.0 
Copyright (C) Microsoft Corporation. All rights reserved. 
... 

(sử dụng Windows 8.1 N x64 Pro; csc quá trình biên dịch đang chạy với 32bits)

sửa đổi cảnh quan không tạo ra hành vi này (ví dụ: thay đổi decimal bởi int, giảm một leve lồng nhau l, ...), thực hiện một lớn Select sau đó giảm, hoạt động tốt

Explicit workaround:

  return base.Values.SelectMany(a => 
         a.Values.SelectMany(b => 
         b.Values.Select (c => 
         c.  Sum  (d => 
         d.  Sum  (e => e.x))))).Min(); 

Mặc dù workaround rõ ràng này tồn tại, nó không được bảo đảm rằng hành vi này sẽ không xảy ra một lần nữa.

Có vấn đề gì?

Cảm ơn bạn!

+1

Ra quan tâm, bạn đã thử điều này với bản xem trước VS2015 ? Nó sẽ là thú vị để biết nếu nó cố định trong Roslyn. –

+0

Mã này có một số điều khiến cho việc suy ra các loại: 1) Chuyển đổi ngầm định 2) Nhiều quá tải 3) lambdas lồng nhau. Nếu bạn cố gắng hết sức, bạn có thể mã hóa các vấn đề SAT hoàn chỉnh về NP thành những thứ này, vì vậy trình biên dịch không thể giải quyết tất cả các vấn đề về độ phân giải quá tải + loại vấn đề inferrence một cách hiệu quả. Nhưng tôi không biết tại sao ví dụ của bạn quá tệ. – CodesInChaos

+0

@JonSkeet Bất kỳ tình nguyện viên nào? (Tôi không thể) Tôi đã tìm kiếm về điều đó nhưng không tìm thấy: ( – josejuan

Trả lời

3

Dường như độ phân giải loại chung không thành công trong trường hợp đó. Thay đổi từ decimal thành int hoạt động một cách tình cờ. Nếu bạn tăng mức độ làm tổ, bạn sẽ thấy rằng nó cũng không thành công cho int. Trên máy x64 của tôi, mã này biên dịch cho cả hai intdecimal và sử dụng khoảng 2,5 GB bộ nhớ, nhưng làm tăng mức độ làm tổ ở mức tràn khi sử dụng bộ nhớ tăng lên 4GB.

Xác định đối số kiểu một cách rõ ràng cho phép biên dịch mã:

class TypeWrapper : Dictionary<int, Dictionary<int, Dictionary<int, Dictionary<int, SomeType[][]>>>> 
{ 
    public decimal minimumX() 
    { 
     return base.Values 
      .Min<Dictionary<int, Dictionary<int, Dictionary<int, SomeType[][]>>>, decimal>(a => a.Values 
       .Min<Dictionary<int, Dictionary<int, SomeType[][]>>, decimal>(b => b.Values 
        .Min<Dictionary<int, SomeType[][]>, decimal>(c => c.Values 
         .Min(d => d 
          .Sum(e => e.Sum(f => f.x)) 
         ) 
        ) 
       ) 
      ); 
    } 
} 

công trình Ngoài ra trình biên dịch khi bạn giảm làm tổ bằng cách giới thiệu biến cục bộ:

class TypeWrapper : Dictionary<int, Dictionary<int, Dictionary<int, Dictionary<int, SomeType[][]>>>> 
{ 
    public decimal minimumX() 
    { 
     Func<Dictionary<int, SomeType[][]>, decimal> inner = (Dictionary<int, SomeType[][]> c) => c.Values 
         .Min(d => d 
          .Sum(e => e.Sum(f => f.x)) 
         ); 

     return base.Values 
      .Min(a => a.Values 
       .Min(b => b.Values 
        .Min(inner) 
       ) 
      ); 
    } 
} 
Các vấn đề liên quan