`stackalloc` 키워드의 실제 사용


134

stackallocC #에서 프로그래밍 하는 동안 실제로 사용한 사람이 있습니까? 내가하는 일을 알고 있지만 코드에 표시되는 유일한 시간은 실수로 발생하는 것 static입니다. 예를 들어 Intellisense는 입력을 시작할 때 제안하기 때문 입니다.

의 사용 시나리오와 관련이 없지만 stackalloc실제로는 앱에서 상당한 양의 레거시 interop을 수행하므로 매번 unsafe코드 사용에 의지 할 수 있습니다. 그럼에도 불구하고 나는 보통 unsafe완전히 피하는 방법을 찾습니다 .

그리고 .Net의 단일 스레드에 대한 스택 크기는 ~ 1Mb (잘못된 경우 수정)이므로을 사용하지 않아도됩니다 stackalloc.

"이것은 내가 안전하지 않고 사용할 수있는 정확한 양의 데이터와 처리 방법"이라고 말할 수있는 실용적인 사례가 stackalloc있습니까?


답변:


150

사용하는 유일한 이유 stackalloc는 성능 (계산 또는 interop)입니다. stackalloc힙 할당 배열 대신 사용 하면 GC 압력이 줄어들고 (GC를 덜 실행해야 함) 배열을 고정 할 필요가 없으며 힙 배열보다 할당 속도가 빠르며 메소드에서 자동으로 해제됩니다. exit (힙 할당 된 배열은 GC가 실행될 때만 할당 해제됩니다). 또한 stackallocmalloc 또는 .Net과 같은 기본 할당 자 대신 스코프 종료시 속도와 자동 할당 해제를 얻습니다.

성능면에서는 stackalloc데이터의 지역성으로 인해 CPU에서 캐시 적중 가능성이 크게 증가합니다.


26
데이터의 지역성, 좋은 지적! 여러 구조 또는 배열을 할당하려고 할 때 관리되는 메모리가 거의 달성하지 못하는 것입니다. 감사!
Groo

22
힙 할당은 일반적으로 트래킹 할 사용 가능한 목록이 없기 때문에 관리 대상 객체에 비해 관리 대상 객체보다 빠릅니다. CLR은 단지 힙 포인터를 증가시킵니다. 지역의 경우 순차적 압축은 힙 압축으로 인해 장기 실행 관리 프로세스에 대해 배치 될 가능성이 높습니다.
Shea

1
"힙 어레이보다 할당하는 것이 더 빠릅니다"왜 그런가요? 그냥 지역? 어느 쪽이든 포인터 범프 일뿐입니다.
Max Barraclough

2
@MaxBarraclough 응용 프로그램 수명 동안 힙 할당에 GC 비용을 추가하기 때문입니다. 총 할당 비용 = 할당 + 할당 해제,이 경우 포인터 범프 + GC 힙, 포인터 범프 + 포인터 감소 스택
Pop Catalin

35

나는 거의 실시간 DSP 작업을 위해 버퍼를 할당하기 위해 stackalloc을 사용했다. 성능이 최대한 일관되어야하는 것은 매우 구체적인 사례였습니다. 일관성과 전체 처리량 사이에는 차이가 있습니다.이 경우 힙 할당이 너무 느리지 않고 프로그램의 해당 시점에서 가비지 수집의 결정적이지 않기 때문에 걱정하지 않았습니다. 99 %의 경우에는 사용하지 않을 것입니다.


25

stackalloc안전하지 않은 코드에만 관련됩니다. 관리 코드의 경우 데이터를 할당 할 위치를 결정할 수 없습니다. 값 유형은 기본값으로 스택에 할당됩니다 (값이 참조 유형의 일부가 아닌 경우 힙에 할당되지 않은 경우). 참조 유형은 힙에 할당됩니다.

일반 바닐라 .NET 애플리케이션의 기본 스택 크기는 1MB이지만 PE 헤더에서이를 변경할 수 있습니다. 스레드를 명시 적으로 시작하는 경우 생성자 오버로드를 통해 다른 크기를 설정할 수도 있습니다. ASP.NET 응용 프로그램의 경우 기본 스택 크기는 256K에 불과하므로 두 환경간에 전환하는 경우 명심해야합니다.


Visual Studio에서 기본 스택 크기를 변경할 수 있습니까?
구성자

@ configurator : 내가 아는 한.
Brian Rasmussen

17

스팬의 스택 할당 초기화. 이전 버전의 C #에서는 stackalloc의 결과를 포인터 로컬 변수에만 저장할 수있었습니다. C # 7.2부터 stackalloc을 표현식의 일부로 사용할 수 있으며 범위를 대상으로 할 수 있으며 unsafe 키워드를 사용하지 않고도 수행 할 수 있습니다. 따라서 글을 쓰는 대신

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

간단하게 작성할 수 있습니다.

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

이것은 작업을 수행하기 위해 약간의 스크래치 공간이 필요하지만 비교적 작은 크기의 힙 메모리 할당을 피하려는 경우에도 매우 유용합니다.

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

출처 : C #-스팬 정보 : 새로운 .NET 메인 스테이 탐색


4
팁 고마워. C #의 각 새 버전은 C ++에 조금 더 가까워지는 것 같습니다. 실제로 IMHO가 좋습니다.
Groo

1
여기여기 에서 볼 수 있듯이 Span.NET 프레임 워크 4.7.2에서는 사용할 수 없으며 4.8에서는 사용할 수 없습니다 ... 따라서 새로운 언어 기능은 현재로서는 제한적으로 사용됩니다.
Frederic

2

이 질문에는 훌륭한 답변이 있지만 그 점을 지적하고 싶습니다.

Stackalloc을 사용하여 네이티브 API를 호출 할 수도 있습니다.

많은 기본 함수는 호출자가 리턴 결과를 얻기 위해 버퍼를 할당해야합니다. 예를 들어 CfGetPlaceholderInfo 함수의 cfapi.h서명은 다음과 같습니다.

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

interop을 통해 C #으로 호출하려면

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

stackalloc을 사용할 수 있습니다.

byte* buffer = stackalloc byte[1024];
CfGetPlaceholderInfo(fileHandle, 0, buffer, 1024, out var returnedLength);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.