Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Viktigt!
De tekniker som beskrivs i det här avsnittet förbättrar prestanda när de tillämpas på heta sökvägar i koden. Heta sökvägar är de avsnitt i din kodbas som körs ofta och upprepade gånger i normala åtgärder. Om du använder dessa tekniker för kod som inte körs ofta får du minimal påverkan. Innan du gör några ändringar för att förbättra prestandan är det viktigt att mäta en baslinje. Analysera sedan baslinjen för att avgöra var minnesflaskhalsar uppstår. Du kan lära dig om många plattformsoberoende verktyg för att mäta programmets prestanda i avsnittet om diagnostik och instrumentation. Du kan öva på en profileringssession i självstudiekursen för att mäta minnesanvändning i Visual Studio-dokumentationen.
När du har mätt minnesanvändningen och har fastställt att du kan minska allokeringen använder du teknikerna i det här avsnittet för att minska allokeringen. Efter varje efterföljande ändring mäter du minnesanvändningen igen. Kontrollera att varje ändring har en positiv inverkan på minnesanvändningen i ditt program.
Prestandaarbete i .NET innebär ofta att ta bort allokeringar från koden. Alla minnesblock som du allokerar måste så småningom frigöras. Färre allokeringar minskar tiden som ägnas åt skräpinsamling. Det ger mer förutsägbar exekveringstid genom att ta bort skräpsamlande processer från specifika kodvägar.
En vanlig taktik för att minska allokeringen är att ändra kritiska datastrukturer från class
typer till struct
typer. Den här ändringen påverkar semantiken för att använda dessa typer. Parametrar och returnvärden skickas nu som värde i stället för med referens. Kostnaden för att kopiera ett värde är försumbar om typerna är små, tre ord eller mindre (med tanke på att ett ord är av naturlig storlek på ett heltal). Det är mätbart och kan ha verklig prestandapåverkan för större typer. För att bekämpa effekten av kopiering kan utvecklare skicka dessa typer genom ref
att få tillbaka den avsedda semantiken.
Med C# ref
-funktionerna kan du uttrycka önskad semantik för struct
typer utan att påverka deras allmänna användbarhet negativt. Före dessa förbättringar behövde utvecklare använda unsafe
konstruktioner med pekare och råminne för att uppnå samma prestandaeffekt. Kompilatorn genererar verifierbart säker kod för de nya ref
relaterade funktionerna.
Verifierbart säker kod innebär att kompilatorn identifierar möjliga buffertöverskridningar eller åtkomst till oallokerat eller frigjort minne. Kompilatorn identifierar och förhindrar vissa fel.
Skicka och returnera med referens
Variabler i C# lagrar värden. I struct
typer är värdet innehållet i en instans av typen. I class
typer är värdet en referens till ett minnesblock som lagrar en instans av typen.
ref
Att lägga till modifieraren innebär att variabeln lagrar referensen till värdet. I struct
typer pekar referensen på lagringen som innehåller värdet. I class
typer pekar referensen på lagringsplatsen som innehåller referensen till minnesblocket.
I C# skickas parametrar till metoder efter värde och returvärden returneras efter värde. Argumentets värde skickas till metoden. Värdet för returargumentet är returvärdet.
Modifieraren ref
, in
, ref readonly
eller out
anger att argumentet skickas med referens. En referens till lagringsplatsen skickas till metoden. Om du lägger ref
till i metodsignaturen returneras returvärdet med referens. En referens till lagringsplatsen är returvärdet.
Du kan också använda referenstilldelning för att få en variabel att referera till en annan variabel. En typisk tilldelning kopierar värdet för den högra sidan av tilldelningen till variabeln på den vänstra sidan av tilldelningen. En referenstilldelning kopierar minnesplatsen för variabeln till höger till variabeln till vänster. Nu ref
refererar till den ursprungliga variabeln:
int anInteger = 42; // assignment.
ref int location = ref anInteger; // ref assignment.
ref int sameLocation = ref location; // ref assignment
Console.WriteLine(location); // output: 42
sameLocation = 19; // assignment
Console.WriteLine(anInteger); // output: 19
När du tilldelar en variabel ändrar du dess värde. När du refererar till en variabel ändrar du vad den refererar till.
Du kan arbeta direkt med lagring för värden med ref
variabler, passera genom referens, och referenstilldelning. Omfångsregler som tillämpas av kompilatorn säkerställer säkerheten när du arbetar direkt med lagring.
Modifierarna ref readonly
och in
anger båda att argumentet ska skickas med referens och inte kan tilldelas om i -metoden. Skillnaden är att ref readonly
metoden använder parametern som en variabel. Metoden kan fånga parametern eller returnera den via skrivskyddad referens. I sådana fall bör du använda ref readonly
modifieraren. Annars erbjuder in
modifieraren mer flexibilitet. Du behöver inte lägga till modifieraren in
i ett argument för in
-parametern, så du kan uppdatera befintliga API-signaturer säkert med hjälp av modifieraren in
. Kompilatorn utfärdar en varning om du inte lägger till antingen ref
eller in
modifieraren till ett argument för en ref readonly
parameter.
Referenssäker kontext
I C# finns det regler för ref
-uttryck som säkerställer att ett ref
-uttryck inte kan nås om den lagring det hänvisar till inte längre är giltig. Tänk på följande exempel:
public ref int CantEscape()
{
int index = 42;
return ref index; // Error: index's ref safe context is the body of CantEscape
}
Kompilatorn rapporterar ett fel eftersom du inte kan returnera en referens till en lokal variabel från en metod. Anroparen kan inte komma åt lagringen som hänvisas till.
Referenssäker kontext definierar omfånget där ett ref
uttryck är säkert att komma åt eller ändra. I följande tabell visas referenssäkra kontexter för variabeltyper.
ref
variabler kan inte deklareras i class
eller utan referens struct
, och därför finns inte dessa rader i tabellen.
Deklaration | referenssäker kontext |
---|---|
icke-refererad lokal | block där lokal variabel deklareras |
icke-referensparameter | nuvarande metod |
ref , ref readonly , in parameter |
anropsmetod |
out -parametern |
nuvarande metod |
class fält |
anropsmetod |
icke-referensfält struct |
nuvarande metod |
ref fält av ref struct |
anropsmetod |
En variabel kan returneras ref
om dess referenssäkra kontext är anropande metod. Om den aktuella metoden eller ett block är dess säker kontext för referens är retur inte tillåten. Följande kodfragment visar två exempel. Ett medlemsfält kan nås från omfånget som kallar på en metod, så ett klass- eller structfälts referenssäkra kontext är den metod som anropar.
Referenssäker kontext för en parameter med modifierarna ref
eller in
är hela metoden. Båda kan returneras ref
från en medlemsmetod:
private int anIndex;
public ref int RetrieveIndexRef()
{
return ref anIndex;
}
public ref int RefMin(ref int left, ref int right)
{
if (left < right)
return ref left;
else
return ref right;
}
Anmärkning
När ref readonly
- eller in
-modifieraren tillämpas på en parameter, kan parametern returneras av ref readonly
, inte av ref
.
Kompilatorn ser till att en referens inte kan komma undan sin referenssäkra kontext. Du kan använda ref
parametrar, ref return
och ref
lokala variabler på ett säkert sätt eftersom kompilatorn identifierar om du av misstag har skrivit kod där ett ref
uttryck kan nås när dess lagring inte är giltig.
Säker kontext och referensstrukturer
ref struct
kräver fler regler för att säkerställa att de kan användas på ett säkert sätt. En ref struct
typ kan innehålla ref
fält. Det kräver införandet av en säker kontext. För de flesta typer är den säkra kontexten den metod som anropar. Med andra ord kan ett värde som inte är ett ref struct
alltid returneras från en metod.
Informellt är den säkra kontexten för en ref struct
omfattningen där alla dess ref
fält kan nås. Med andra ord är det skärningspunkten mellan referenssäkra kontexten för alla dess ref
fält. Metoden nedan returnerar ett ReadOnlySpan<char>
till ett medlemsfält, vilket gör att dess säkra kontext är metoden:
private string longMessage = "This is a long message";
public ReadOnlySpan<char> Safe()
{
var span = longMessage.AsSpan();
return span;
}
Däremot genererar följande kod ett fel eftersom ref field
medlemmen Span<int>
i refererar till den stackallokerade matrisen med heltal. Det går inte att komma ifrån metoden:
public Span<int> M()
{
int length = 3;
Span<int> numbers = stackalloc int[length];
for (var i = 0; i < length; i++)
{
numbers[i] = i;
}
return numbers; // Error! numbers can't escape this method.
}
Förena minnestyper
Introduktionen av System.Span<T> och System.Memory<T> ger en enhetlig modell för att arbeta med minne.
System.ReadOnlySpan<T> och System.ReadOnlyMemory<T> tillhandahåller skrivskyddade versioner för minnesåtkomst. De ger alla en abstraktion över ett minnesblock som lagrar en matris med liknande element. Skillnaden är att Span<T>
och ReadOnlySpan<T>
är ref struct
typer medan Memory<T>
och ReadOnlyMemory<T>
är struct
typer. Intervall innehåller ett ref field
. Därför kan instanser av ett spann inte lämna sin säkra kontext. Den säkra kontexten för en ref struct
är referenssäker kontext för dess ref field
. Implementeringen av Memory<T>
och ReadOnlyMemory<T>
ta bort den här begränsningen. Du använder dessa typer för direkt åtkomst till minnesbuffertar.
Förbättra prestanda med referenssäkerhet
Att använda dessa funktioner för att förbättra prestandan omfattar följande uppgifter:
-
Undvik allokeringar: När du ändrar en typ från en
class
till enstruct
ändrar du hur den lagras. Lokala variabler lagras i stacken. Medlemmar lagras infogade när containerobjektet allokeras. Den här ändringen innebär färre allokeringar och det minskar arbetet som skräpinsamlaren utför. Det kan också minska minnestrycket så att sophämtaren körs mindre ofta. -
Bevara referenssemantik: Om du ändrar en typ från en
class
till enstruct
ändras semantiken för att skicka en variabel till en metod. Kod som ändrade tillståndet för dess parametrar behöver ändras. Nu när parametern är enstruct
ändrar metoden en kopia av det ursprungliga objektet. Du kan återställa den ursprungliga semantiken genom att skicka parametern som enref
parameter. Efter den ändringen ändrar metoden originaletstruct
igen. -
Undvik att kopiera data: Kopiering av större
struct
typer kan påverka prestanda i vissa kodsökvägar. Du kan också lägga tillref
modifieraren för att skicka större datastrukturer till metoder med referens i stället för efter värde. -
Begränsa ändringar: När en
struct
typ skickas med referens kan den anropade metoden ändra structens tillstånd. Du kan ersättaref
modifieraren medref readonly
modifierarna ellerin
för att ange att argumentet inte kan ändras. Föredraref readonly
när metoden hanterar parametern eller returnerar den som en skrivskyddad referens. Du kan också skapareadonly struct
typer ellerstruct
typer medreadonly
medlemmar för att ge mer kontroll över vilka medlemmar i enstruct
som kan ändras. -
Manipulera minnet direkt: Vissa algoritmer är mest effektiva när datastrukturer behandlas som ett minnesblock som innehåller en sekvens med element. Typerna
Span
ochMemory
ger säker åtkomst till minnesblock.
Ingen av dessa tekniker kräver unsafe
kod. Används klokt, kan du få prestandaegenskaper från säker kod som tidigare endast var möjligt att uppnå med hjälp av osäkra tekniker. Du kan prova teknikerna själv i självstudien om att minska minnesallokeringen.