`Stackalloc` anahtar kelimesinin pratik kullanımı


134

stackallocC # programlama sırasında hiç kimse gerçekten kullanılmış ? Ne yaptığının farkındayım, ancak kodumda göründüğü tek zaman yanlışlıkla, çünkü Intellisense yazmaya başladığımda bunu önerir static.

Her ne kadar kullanım senaryoları ile ilgili olmasa da, stackallocaslında uygulamalarımda önemli miktarda eski birlikte çalışma yapıyorum, bu yüzden her zaman ve sonra unsafekod kullanmaya başvurabilirim . Ancak yine de genellikle unsafetamamen kaçınmanın yollarını bulurum .

Ve .Net içinde tek bir iş parçacığı için yığın boyutu ~ 1Mb olduğundan (yanlışsam beni düzelt), kullanmaktan daha da saklıyım stackalloc.

Birisinin söyleyebileceği bazı pratik durumlar var mı: "Bu, güvensiz hale gelip kullanmam için doğru miktarda veri ve işlemdir stackalloc"?


5
Sadece fark System.Numbersbunu çok kullanır referencesource.microsoft.com/#mscorlib/system/...
SLAI

Yanıtlar:


150

Kullanmanın tek nedeni stackallocperformanstır (hesaplamalar veya birlikte çalışma için). stackallocYığın için ayrılmış bir dizi yerine kullanarak , daha az GC basıncı oluşturursunuz (GC'nin daha az çalışması gerekir), dizileri sabitlemenize gerek yoktur, bir yığın dizisinden daha hızlı tahsis edilir, otomatik olarak yöntemle serbest bırakılır çıkış (yığın tahsisli diziler yalnızca GC çalıştırıldığında serbest bırakılır). Ayrıca, stackallocyerel bir ayırıcı (malloc veya .Net eşdeğeri gibi) yerine kullanarak , kapsam çıkışında hız ve otomatik bölme elde edersiniz.

Performans açısından akıllıca kullanırsanız stackalloc, verilerin yeri nedeniyle CPU'da önbellek isabet şansını büyük ölçüde artırır.


26
Verilerin yeri, iyi bir nokta! Birkaç yapı ya da dizi tahsis etmek istediğinizde yönetilen hafıza nadiren bunu başaracaktır. Teşekkürler!
Groo

22
Yığın ayırmaları, yönetilecek nesneler için genellikle yönetilemeyenlerden daha hızlıdır; CLR yığın işaretçisini arttırır. Yöreye gelince, sıralı ayırmaların yığın sıkıştırması nedeniyle uzun süredir yönetilen süreçler için konumlandırılması daha olasıdır.
Shea

1
"Öbek dizisinden daha hızlı tahsis etmek" Neden? Sadece yerellik mi? Her iki durumda da sadece bir işaretçi çarpması, değil mi?
Max Barraclough

2
@MaxBarraclough Çünkü GC maliyetini uygulama ömrü boyunca yığın tahsislerine eklersiniz. Toplam tahsis maliyeti = tahsis + deallocation, bu durumda işaretçi yumru + GC Heap, işaretçi yumru + işaretçi azaltma Stack
Pop Catalin

35

[Yakın] gerçek zamanlı DSP çalışması için tampon ayırmak için stackalloc kullandım. Performansın mümkün olduğunca tutarlı olması gereken çok özel bir durumdu. Tutarlılık ve genel verim arasında bir fark olduğunu unutmayın - bu durumda, yığın tahsislerinin çok yavaş olmasıyla ilgilenmedim, sadece programın o noktasında çöp toplama determinizmi ile. Vakaların% 99'unda kullanmam.


25

stackallocyalnızca güvenli olmayan kodlar için geçerlidir. Yönetilen kod için nereye veri ayıracağınıza karar veremezsiniz. Değer türleri varsayılan olarak yığına ayrılır (bir başvuru türünün parçası olmadıkları sürece, bu durumda öbek üzerinde ayrılırlar). Referans türleri öbek üzerinde tahsis edilir.

Düz bir vanilya .NET uygulaması için varsayılan yığın boyutu 1 MB'dir, ancak bunu PE başlığında değiştirebilirsiniz. İş parçacıklarını açıkça başlatıyorsanız, yapıcı aşırı yüklemesi yoluyla farklı bir boyut da ayarlayabilirsiniz. ASP.NET uygulamaları için varsayılan yığın boyutu yalnızca 256K'dır; bu, iki ortam arasında geçiş yapıyorsanız akılda tutulması gereken bir şeydir.


Visual Studio'dan varsayılan yığın boyutunu değiştirmek mümkün mü?
yapılandırıcı

@configurator: Fark ettiğim kadarıyla değil.
Brian Rasmussen

17

Açıklıkların yığınsal olarak başlatılması. C # 'ın önceki sürümlerinde, stackalloc sonucu yalnızca işaretçi yerel değişkeninde saklanabilir. C # 7.2'den itibaren, stackalloc artık bir ifadenin parçası olarak kullanılabilir ve bir yayılma alanını hedefleyebilir ve bu güvenli olmayan anahtar sözcük kullanılmadan yapılabilir. Böylece yazmak yerine

Span<byte> bytes;
unsafe
{
  byte* tmp = stackalloc byte[length];
  bytes = new Span<byte>(tmp, length);
}

Basitçe yazabilirsiniz:

Span<byte> bytes = stackalloc byte[length];

Bu, bir işlemi gerçekleştirmek için biraz çalışma alanına ihtiyaç duyduğunuz, ancak nispeten küçük boyutlar için yığın bellek ayırmaktan kaçınmak istediğiniz durumlarda da son derece yararlıdır

Span<byte> bytes = length <= 128 ? stackalloc byte[length] : new byte[length];
... // Code that operates on the Span<byte>

Kaynak: C # - Span hakkında her şey: Yeni bir .NET Mainstay keşfetmek


4
Bahşiş için teşekkürler. C # 'ın her yeni sürümü aslında IMHO iyi bir şey olan C ++' a biraz daha yakın görünüyor.
Groo

1
Görüldüğü üzere burada ve burada , Spanyeni bir dil özelliği şu an için sınırlı kullanım hala Böylece ... .NET framework 4.7.2 yılında mevcut değil ve hatta değil 4.8 de ne yazık olduğunu.
Frederic

2

Bu soruda harika cevaplar var ama şunu belirtmek istiyorum

Stackalloc ayrıca yerel API'leri çağırmak için de kullanılabilir

Birçok yerel işlev, çağıranın sonucu almak için arayanın bir tampon tahsis etmesini gerektirir. Örneğin, içindeki CfGetPlaceholderInfo işlevi cfapi.haşağıdaki imzayı taşır .

HRESULT CfGetPlaceholderInfo(
HANDLE                    FileHandle,
CF_PLACEHOLDER_INFO_CLASS InfoClass,
PVOID                     InfoBuffer,
DWORD                     InfoBufferLength,
PDWORD                    ReturnedLength);

Birlikte çalışarak C # olarak çağırmak için,

[DllImport("Cfapi.dll")]
public static unsafe extern HResult CfGetPlaceholderInfo(IntPtr fileHandle, uint infoClass, void* infoBuffer, uint infoBufferLength, out uint returnedLength);

Stackalloc'u kullanabilirsiniz.

byte* buffer = stackalloc byte[1024];
CfGetPlaceholderInfo(fileHandle, 0, buffer, 1024, out var returnedLength);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.