Чому компілятор C # божеволіє від цього вкладеного запиту LINQ?


97

Спробуйте скомпілювати наступний код, і ви побачите, що компілятор займає> 3 ГБ оперативної пам'яті (вся вільна пам'ять на моїй машині) і дуже тривалий час для компіляції (насправді я отримую виняток IO через 10 хвилин).

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)))))));
    }
}

Хтось може пояснити цю цікаву поведінку?

Версія CS: Microsoft (R) Visual C # Compiler версії 4.0.30319.17929
Назва ОС: Microsoft Windows 7 Ultimate
Версія ОС: 6.1.7601 з пакетом оновлень 1, збірка 7601

Використання пам'яті


5
Гарний дзвінок! Я просто вставив код у візуальну студію, і він зайняв усі 4 Гб, що дозволено 32-розрядному процесу, а потім зірвався (2013 Ultimate у Windows 8.1).
satnhak

2
Додайте цей код до загальної кодової бази (за допомогою блокнота) і спостерігайте, як машини ваших колег виходять з ладу.
usr

3
Звучить добре, як повідомляти про Microsoft Connect і перед командою Roslyn, якщо їх компілятор виявляє однакову поведінку.
Трілліан

3
Я вважаю, що я десь чув, як Ерік Ліпперт говорив (хоч і не пам’ятаю, де), що вкладання лямбд занадто часто з висновками типу може викликати у компілятора неприємні головні болі. Я не можу подумати, де я це бачив, хоча не можу навести посилання. Сподіваємось, сам чоловік може це побачити і прокоментувати ...
Кріс,

2
Молодці, обріжте це, і у вас може бути приємна відповідь на це: розбийте ваш улюблений компілятор
Натан Купер

Відповіді:


40

Я вважаю, що це пов’язано з висновком типу та / або генерацією лямбда-сигналу (коли висновок типу має йти в протилежному напрямку до нормального), у поєднанні з роздільною здатністю перевантаження. На жаль, просто надання параметрів типу не допомагає ситуації (де йому, мабуть, все-таки потрібно виконати перевірку типу).

Наступний код, який логічно повинен бути еквівалентним вашому, після аналізу лямбдас, компілюється без проблем:

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;
}

Я вважаю, що Ерік Ліпперт розміщував до цього висновок про те, що це одне з місць у компіляторі C #, де (певні проблеми) можуть змусити компілятор спробувати вирішити проблему NP-Complete, а його єдиною реальною стратегією (як тут) є груба сила. Якщо я зможу знайти відповідні посилання, я додаю їх тут.


Найкраще посилання, яке я можу знайти, є тут, де Ерік обговорює той факт, що саме робота з вирішення перевантажень спричиняє реальну вартість - пам’ятайте, Enumerable.Sum має 10 перевантажень, які приймають лямбда / метод.


1
Отже, в основному компілятор насильно пробиває собі шлях через 10^nкомбінації (де nкількість ланцюгових методів). Звучить розумно (як пояснення, тобто).
DarkWanderer

1
@DarkWanderer:that^numberofpossibletypes
леппі

@Damien_The_Unbeliever, я розумію ваше мислення, шви справді розумні (до речі, код не
складається

15
Ваш аналіз тут правильний. Кожна лямбда вводить коефіцієнт десяти можливих перевантажень, і всі комбінації повинні бути перевірені. Я розглядав можливість додавання коду, який виходив на допомогу, коли кількість комбінацій збільшувалася, але так і не закінчилося його реалізацією.
Ерік Ліпперт,

2
@EricLippert Час на запит на витяг! : D
Роб Х
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.