2014-12-17 10 views
9

Ho riscontrato uno spiacevole comportamento durante lo sviluppo con Visual Studio. Stava appendendo la mia macchina durante la compilazione di C#.Compilatore di compilazione C# (csc.exe) compilazione di tipi nidificati di overflow e Linq

ho ridotto il comportamento a quello successivo codice minimo fonte

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

compilazione con

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. 
... 

(utilizzando Windows 8.1 x64 Pro N; csc processo compilatore è in esecuzione con 32bit)

le modifiche alla vista non producono questo comportamento (ad esempio cambiando decimal per int, riducendo un livello nidificato l, ...), eseguendo un grande Select riducendo poi, funziona bene

soluzione esplicito:

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

Sebbene questa soluzione esplicito esiste, non è garantito che questo comportamento non si verificherà di nuovo.

Cosa c'è che non va?

Grazie!

+1

Fuori di interesse, avete provato questo con l'anteprima VS2015 ? Sarebbe interessante sapere se è stato risolto in Roslyn. –

+0

Questo codice ha diverse cose che rendono difficile dedurne i tipi: 1) Conversioni implicite 2) Molti sovraccarichi 3) lambda annidate. Se ci provi abbastanza, puoi codificare i problemi NP completi di SAT in questi, quindi il compilatore non può risolvere in modo efficiente tutta la risoluzione di sovraccarico + tipo i problemi di inferenza. Ma non so perché il tuo esempio sia così cattivo. – CodesInChaos

+0

@JonSkeet Qualche volontario? (Non posso) Ho cercato su questo ma non trovato: ( – josejuan

risposta

3

Sembra che la risoluzione del tipo generico non riesca in questo caso. Il passaggio da decimal a int funziona per caso. Se aumenti il ​​livello di nidificazione, vedrai che fallisce anche per int. Sulla mia macchina x64 questo codice viene compilato sia per int sia per decimal e utilizza circa 2,5 GB di memoria, ma l'aumento del livello di nidificazione risulta in overflow quando l'utilizzo della memoria aumenta fino a 4 GB.

Specificando tipo di argomento consente esplicitamente di compilare il codice:

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

opere compilatore Anche quando si riduce nidificano introducendo variabile locale:

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