2009-07-14 16 views
4

fand ich einen Blog-Eintrag, dass auf dem Stapel anstelle des Haufens kann beschließen, setzen Array manchmal C# -Compiler schlägt vor:Entscheidet sich der C# -Compiler dafür, stackalloc selbst zu verwenden?

Improving Performance Through Stack Allocation (.NET Memory Management: Part 2)

Dieser Kerl, dass behauptet:

Der Compiler auch wird manchmal beschließen, Dinge selbst auf den Stapel zu legen. Ich habe ein Experiment mit TestStruct2 gemacht, in dem ich ihm einen unsicheren und normalen Kontext zugewiesen habe. Im unsicheren Kontext wurde das Array auf den Heap gestellt, aber im normalen Kontext, als ich in den Speicher schaute, war das Array tatsächlich auf dem Stack zugewiesen worden.

Kann jemand das bestätigen?
Ich habe versucht, sein Beispiel zu wiederholen, aber jedes Mal versuchte ich Array wurde auf dem Haufen zugeordnet.

Wenn C# -Compiler kann so einen Trick ohne Verwendung von "unsicheren" Schlüsselwort bin ich besonders daran interessiert. Ich habe einen Code, der an vielen kleinen Byte-Arrays arbeitet (8-10 Byte lang) und daher ist die Verwendung von Heap für jedes neue Byte [...] eine Verschwendung von Zeit und Arbeitsspeicher (insbesondere, dass jedes Objekt auf Heap 8 Byte Overhead hat) benötigt für Müllsammler).

EDIT: Ich möchte nur erklären, warum es mir wichtig ist:
Ich schreibe Bibliothek, die mit Gemalto.NET Smartcard in Verbindung steht, die .NET-Code arbeitet darin haben kann. Wenn ich eine Methode aufrufen, die etwas zurückgibt, gibt die Smartcard 8 Bytes zurück, die mir den genauen Typ des Rückgabewerts beschreiben. Diese 8 Bytes werden unter Verwendung von md5-Hash- und einigen Byte-Array-Verkettungen berechnet.
Problem ist, dass, wenn ich ein Array habe, das mir nicht bekannt ist, ich alle Typen in allen Assemblys, die in der Anwendung geladen werden, scannen muss, und für jeden muss ich diese 8 Byte berechnen, bis ich dasselbe Array finde.
Ich kenne keinen anderen Weg, den Typ zu finden, also versuche ich, es so viel wie möglich zu beschleunigen.

Antwort

2

Von Ihrer Linie:

Ich habe einen Code, der auf viele kleine Byte-Arrays (8-10 Bytes lang)

persönlich arbeitet, würde ich mehr daran interessiert sein, ein bei der Zuteilung Ersatzpuffer irgendwo, den verschiedene Teile Ihres Codes wiederverwenden können (während der gleiche Block verarbeitet wird). Dann müssen Sie sich keine Sorgen um GC/Creation machen. In den meisten Fällen (wo der Puffer für sehr diskrete Operationen verwendet wird) mit einem Scratch-Buffer, können Sie sogar immer davon ausgehen, dass es "alles von Ihnen" ist - d. H.Jede Methode, die sie benötigt, kann davon ausgehen, dass sie mit null anfangen kann.

Ich benutze diese Single-Puffer-Ansatz in einigen binären Serialisierungscode (während der Codierung von Daten); Es ist ein großer Schub für die Leistung. In meinem Fall übergebe ich ein "Kontext" -Objekt zwischen den Ebenen der Serialisierung (die den Scratch-Puffer einkapselt, dem Ausgabestrom (mit etwas zusätzlicher lokaler Pufferung) und einigen anderen Kuriositäten).

+0

Weil thesee scannen kann einfach Pipelined sein schrieb ich Erweiterung Methode, die dieses Array nur einmal zugewiesen und Rendite verwendet. Danke für den Vorschlag :-) – SeeR

1

System.Array (die Klasse, die ein Array darstellt) ist ein Referenztyp und lebt auf dem Heap. Sie können nur ein Array auf dem Stapel haben, wenn Sie unsicheren Code verwenden.

Ich kann nicht sehen, wo es sonst in dem Artikel, auf den Sie sich beziehen, steht. Wenn Sie einen Stapel zugeordnete Array haben wollen, können Sie etwas tun:

decimal* stackAllocatedDecimals = stackalloc decimal[4]; 

Persönlich würde ich nicht lästige, wie viel Leistung denken Sie, Sie von diesem Ansatz gewinnen?

Diese CodeProject article könnte Ihnen jedoch nützlich sein.

+1

In einer engen Schleife kann die Leistungsverbesserung sehr groß sein. Wenn Sie wiederholte Heapzuweisungen vermeiden, wird auch die Heapfragmentierung verhindert. Das ist ungefähr dasselbe, warum es sinnvoll ist, einen StringBuilder zu verwenden, anstatt wiederholt neue unveränderliche Instanzen der String-Klasse zu erstellen, die dann vom Garbage Collector aufgeräumt werden müssen. –

+1

@ Rick - sicher, es könnte die Leistung sehr verbessern - es sei denn, Sie profilieren die App, obwohl Sie nicht wissen, ob dies die Ursache für ein Leistungsproblem ist. – RichardOD

4

Autor des verlinkten Artikels hier.

Es scheint unmöglich, Stapelzuordnung außerhalb eines unsicheren Kontexts zu erzwingen. Dies ist wahrscheinlich der Fall, um einige Klassen der Stapelüberlaufbedingung zu verhindern.

Stattdessen empfehle ich die Verwendung einer Speicher-Recycler-Klasse, die Byte-Arrays nach Bedarf zuweist, aber auch ermöglicht, sie später zur Wiederverwendung "einzuschalten". Es ist so einfach, einen Stapel unbenutzter Byte-Arrays zu halten und, wenn die Liste leer ist, neue zu reservieren.

Stack<Byte[]> _byteStack = new Stack<Byte[]>(); 

Byte[] AllocateArray() 
{ 
Byte[] outArray; 
if (_byteStack.Count > 0) 
    outArray = _byteStack.Pop(); 
else 
    outArray = new Byte[8]; 
return outArray; 
} 

void RecycleArray(Byte[] inArray) 
{ 
    _byteStack.Push(inArray); 
} 

Wenn Sie versuchen, mit einer Art, die einen Hash zu passen scheint es die beste Idee, ein Wörterbuch für die schnelle Lookups zu verwenden wäre. In diesem Fall könnten Sie alle relevanten Typen beim Start laden. Wenn dies dazu führt, dass der Programmstart zu langsam wird, sollten Sie in Erwägung ziehen, sie bei der ersten Verwendung jedes Typs zwischenzuspeichern.

Verwandte Themen