Tại sao trình biên dịch C # lại phát điên với truy vấn LINQ lồng nhau này?


97

Hãy thử biên dịch mã sau và bạn sẽ thấy rằng trình biên dịch chiếm> 3 GB RAM (tất cả bộ nhớ trống trên máy của tôi) và thời gian biên dịch rất lâu (thực sự tôi nhận được ngoại lệ IO sau 10 phút).

using System;
using System.Linq;

public class Test
{
    public static void Main()
    {
        Enumerable.Range(0, 1).Sum(a =>
        Enumerable.Range(0, 1).Sum(b =>
        Enumerable.Range(0, 1).Sum(c =>
        Enumerable.Range(0, 1).Sum(d =>
        Enumerable.Range(0, 1).Sum(e =>
        Enumerable.Range(0, 1).Sum(f =>
        Enumerable.Range(0, 1).Count(g => true)))))));
    }
}

Ai có thể giải thích hành vi tò mò này?

Phiên bản CS: Microsoft (R) Visual C # Compiler phiên bản 4.0.30319.17929
Tên hệ điều hành: Microsoft Windows 7 Ultimate
Phiên bản hệ điều hành: 6.1.7601 Gói dịch vụ 1 Bản dựng 7601

Sử dụng bộ nhớ


5
Tốt cuộc gọi! Tôi vừa dán mã vào studio trực quan và nó tiêu thụ tất cả 4Gb mà quy trình 32 bit được phép và sau đó bị lỗi (2013 Ultimate trên Windows 8.1).
satnhak

2
Thêm mã này vào cơ sở mã dùng chung (sử dụng notepad) và xem máy đồng nghiệp của bạn gặp sự cố.
usr

3
Nghe có vẻ là một điều tốt để báo cáo về Microsoft Connect và cho nhóm Roslyn nếu trình biên dịch của họ có cùng hành vi.
Trillian

3
Tôi tin rằng tôi đã nghe Eric Lippert nói ở đâu đó (mặc dù tôi không nhớ ở đâu) rằng việc lồng các lambdas quá thường xuyên với kiểu suy luận có thể gây ra một số cơn đau đầu khó chịu cho trình biên dịch. Tôi không thể nghĩ rằng tôi đã nhìn thấy nó ở đâu mặc dù vậy không thể trích dẫn một tài liệu tham khảo. Hy vọng rằng bản thân người đàn ông có thể nhìn thấy điều này và nhận xét ...
Chris

2
Tốt lắm, cắt nó xuống và bạn có thể có một câu trả lời tốt đẹp cho này: Tai nạn biên dịch yêu thích của bạn
Nathan Cooper

Câu trả lời:


40

Tôi tin rằng nó liên quan đến suy luận kiểu và / hoặc tạo lambda (khi suy luận kiểu phải đi theo hướng ngược lại với bình thường), kết hợp với giải quyết quá tải. Thật không may, việc chỉ cung cấp các tham số kiểu không giúp ích gì cho tình hình (có lẽ nó vẫn phải thực hiện kiểm tra kiểu).

Mã sau, về mặt logic phải là mã tương đương từ mã của bạn, sau khi lambdas đã được phân tích, biên dịch mà không có vấn đề gì:

static void Main()
{
    var x = Enumerable.Range(0, 1).Sum(a);
}

private static int a(int a)
{
    return Enumerable.Range(0, 1).Sum(b);
}
private static int b(int b)
{
    return Enumerable.Range(0, 1).Sum(c);
}
private static int c(int c)
{
    return Enumerable.Range(0, 1).Sum(d);
}
private static int d(int d)
{
    return Enumerable.Range(0, 1).Sum(e);
}
private static int e(int e)
{
    return Enumerable.Range(0, 1).Sum(f);
}
private static int f(int f)
{
    return Enumerable.Range(0, 1).Count(g);
}
private static bool g(int g)
{
    return true;
}

Tôi tin rằng Eric Lippert đã đăng trước khi suy luận kiểu đó là một trong những nơi trong trình biên dịch C # nơi (một số vấn đề nhất định) có thể buộc trình biên dịch cố gắng giải quyết một vấn đề NP-Complete và chiến lược thực sự duy nhất của nó (như ở đây) là bạo lực. Nếu tôi có thể tìm thấy các tài liệu tham khảo liên quan, tôi sẽ thêm chúng ở đây.


Tài liệu tham khảo tốt nhất mà tôi có thể tìm thấy ở đây là nơi Eric đang thảo luận về thực tế rằng chính công việc giải quyết quá tải gây ra chi phí thực sự - hãy nhớ rằng, Enumerable.Sum có 10 quá tải chấp nhận một lambda / method.


1
Vì vậy, về cơ bản, trình biên dịch bruteforces theo cách của mình thông qua các 10^nkết hợp ( nsố lượng các phương thức chuỗi). Nghe có vẻ hợp lý (như một lời giải thích, đó là).
DarkWanderer

1
@DarkWanderer:that^numberofpossibletypes
leppie

@Damien_The_Unbeliever, tôi hiểu suy nghĩ của bạn, vỉa thực sự hợp lý (bằng cách này mã không biên dịch )
Eugene D. Gubenkov

15
Phân tích của bạn ở đây là chính xác. Mỗi lambda giới thiệu một hệ số của mười lần quá tải có thể xảy ra và tất cả các kết hợp phải được kiểm tra. Tôi đã cân nhắc việc thêm mã được giải cứu khi số lượng kết hợp lớn nhưng không bao giờ kết thúc việc triển khai nó.
Eric Lippert

2
@EricLippert Đã đến lúc yêu cầu kéo! : D
Rob H
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.