2015-01-28 8 views
14

Analizzando l'allocazione di memoria .NET del mio codice con la procedura guidata di prestazioni di Visual Studio 2013 ho notato una certa funzione di assegnazione di un sacco di byte (dal momento che è chiamato in un ciclo di grandi dimensioni). Ma guardando la funzione evidenziata nel rapporto di profilazione non ho capito perché stia allocando alcun tipo di memoria.non esecuzione di LINQ che causano l'allocazione di memoria C#

Per capire meglio quello che è successo ho isolato il codice che causa le allocazioni. Questo era simile alla classe LinqAllocationTester di seguito.

volta ho commentato il codice LINQ in quella funzione, mai eseguito comunque nel percorso codice testato, nessuna memoria è stato allocato più. La classe NonLinqAllocationTester imita questo comportamento. Sostituendo il codice LINQ con un ciclo normale, non è consentito allocare alcuna memoria.

Se eseguo il test di allocazione della memoria .NET sul codice di test sottostante, viene indicato che LinqAllocationTester causa 100.000 allocazioni (1 per chiamata), mentre NonLinqAllocationTester non ne ha. Nota che useLinq è sempre falso, quindi il codice LINQ non viene mai effettivamente eseguito.

Function Name     | Inclusive | Exclusive | Inclusive | Exclusive 
           | Allocations | Allocations | Bytes  | Bytes 
------------------------------------------------------------------------------------- 
LinqAllocationTester.Test(int32) |  100.000 |  100.000 | 1.200.000 | 1.200.000 
Program.Main(string[])   |  100.000 |   0 | 1.200.000 |   0 

Allora perché la non esecuzione di LINQ allocazioni di codice di causa di memoria? E c'è un modo per evitare questo oltre ad evitare le funzioni LINQ?

class Program { 
    static void Main(string[] args) { 
     List<int> values = new List<int>() { 1, 2, 3, 4 }; 
     LinqAllocationTester linqTester = new LinqAllocationTester(false, values); 
     NonLinqAllocationTester nonLinqTester = new NonLinqAllocationTester(false, values); 

     for (int i = 0; i < 100000; i++) { 
      linqTester.MaxDifference(i); 
     } 

     for (int i = 0; i < 100000; i++) { 
      nonLinqTester.MaxDifference(i); 
     } 
    } 
} 

internal class LinqAllocationTester { 
    private bool useLinq; 
    private List<int> values; 

    public LinqAllocationTester(bool useLinq, List<int> values) { 
     this.useLinq = useLinq; 
     this.values = values; 
    } 

    public int MaxDifference(int value) { 
     if (useLinq) { 
      return values.Max(x => Math.Abs(value - x)); 
     } else { 
      int maxDifference = int.MinValue; 
      foreach (int value2 in values) { 
       maxDifference = Math.Max(maxDifference, Math.Abs(value - value2)); 
      } 
      return maxDifference; 
     } 
    } 
} 

internal class NonLinqAllocationTester { 
    private bool useLinq; 
    private List<int> values; 

    public NonLinqAllocationTester(bool useLinq, List<int> values) { 
     this.useLinq = useLinq; 
     this.values = values; 
    } 

    public int MaxDifference(int value) { 
     if (useLinq) { 
      return 0; 
     } else { 
      int maxDifference = int.MinValue; 
      foreach (int value2 in values) { 
       maxDifference = Math.Max(maxDifference, Math.Abs(value - value2)); 
      } 
      return maxDifference; 
     } 
    } 
} 

risposta

7

È possibile dare uno sguardo alla IL generato per vedere che la DisplayClass per l'espressione LINQ verrà inizializzato all'inizio del metodo al di fuori della prima if-ramo. Questo perché sta generando la chiusura per l'espressione lambda all'inizio del metodo (dove appare il valore per primo).

IL:

IL_0000: ldnull 
IL_0001: stloc.2 
IL_0002: newobj instance void ConsoleApplication2.LinqAllocationTester/'<>c__DisplayClass2'::.ctor() 
IL_0007: stloc.3 
IL_0008: ldloc.3 
IL_0009: ldarg.1 
IL_000a: stfld int32 ConsoleApplication2.LinqAllocationTester/'<>c__DisplayClass2'::'value' 
IL_000f: nop 
IL_0010: ldarg.0 
IL_0011: ldfld bool ConsoleApplication2.LinqAllocationTester::useLinq 
IL_0016: ldc.i4.0 
IL_0017: ceq 
IL_0019: stloc.s CS$4$0001 
IL_001b: ldloc.s CS$4$0001 
IL_001d: brtrue.s IL_0042 

Se si copia il tuo valore a una variabile con scope più stretto in questo modo:

if (useLinq) 
{ 
    int value2 = value; 
    return values.Max(x => Math.Abs(value2 - x)); 
} 

gli stanziamenti aggiuntivi non dovrebbero accadere più.

Problemi correlati