Span<T>
가능한 경우 힙 할당을 피하기 위해 라이브러리를 리팩토링하고 있지만 이전 프레임 워크를 대상으로하므로 일반적인 대체 솔루션도 구현하고 있습니다. 그러나 지금 이상한 문제가 발견되어 .NET Core 3에서 버그를 발견했는지 아니면 불법적 인 일을하고 있는지 확실하지 않습니다.
문제:
// This returns 1 as expected but cannot be used in older frameworks:
private static uint ReinterpretNew()
{
Span<byte> bytes = stackalloc byte[4];
bytes[0] = 1; // FillBytes(bytes);
// returning bytes as uint:
return Unsafe.As<byte, uint>(ref bytes.GetPinnableReference());
}
// This returns garbage in .NET Core 3.0 with release build:
private static unsafe uint ReinterpretOld()
{
byte* bytes = stackalloc byte[4];
bytes[0] = 1; // FillBytes(bytes);
// returning bytes as uint:
return *(uint*)bytes;
}
흥미롭게도 ReinterpretOld
.NET Framework 및 .NET Core 2.0에서 잘 작동하지만 결국 행복 할 수 있습니다.
Btw. ReinterpretOld
.NET Core 3.0에서도 약간 수정하여 수정할 수 있습니다.
//return *(uint*)bytes;
uint* asUint = (uint*)bytes;
return *asUint;
내 질문:
버그입니까, 아니면 ReinterpretOld
우연히 이전 프레임 워크에서 작동합니까? 수정 사항도 적용해야합니까?
비고 :
- 디버그 빌드는 .NET Core 3.0에서도 작동합니다.
- 나는 적용하는 시도
[MethodImpl(MethodImplOptions.NoInlining)]
를ReinterpretOld
하지만 아무런 영향을 미치지 않습니다.
답변
오, 이것은 재미있는 발견입니다. 여기에서 일어나고있는 것은 지역이 멀리 최적화지고 있다는 것입니다 – 남아있는 지역 주민, 아니이 있음을 의미가없는 .locals init
것을 의미 stackalloc
동작합니다는 다르게 , 그리고 공간을 닦지 않고는,
private static unsafe uint Reinterpret1()
{
byte* bytes = stackalloc byte[4];
bytes[0] = 1;
return *(uint*)bytes;
}
private static unsafe uint Reinterpret2()
{
byte* bytes = stackalloc byte[4];
bytes[0] = 1;
uint* asUint = (uint*)bytes;
return *asUint;
}
된다 :
.method private hidebysig static uint32 Reinterpret1() cil managed
{
.maxstack 8
L_0000: ldc.i4.4
L_0001: conv.u
L_0002: localloc
L_0004: dup
L_0005: ldc.i4.1
L_0006: stind.i1
L_0007: ldind.u4
L_0008: ret
}
.method private hidebysig static uint32 Reinterpret2() cil managed
{
.maxstack 3
.locals init (
[0] uint32* numPtr)
L_0000: ldc.i4.4
L_0001: conv.u
L_0002: localloc
L_0004: dup
L_0005: ldc.i4.1
L_0006: stind.i1
L_0007: stloc.0
L_0008: ldloc.0
L_0009: ldind.u4
L_000a: ret
}
내가 생각하는 바람직하지 않은 부작용과 행동 주어진 :이 컴파일러의 버그, 또는 적어도 말을 드리겠습니다 이전 결정 “이 .locals는 init을 방출 ‘라고하는 장소에 배치되고 , 특히 시도하고 stackalloc
제정신을 유지하십시오 -그러나 컴파일러 사람들의 동의 여부는 그들에게 달려 있습니다.
해결 방법은 stackalloc
공간을 정의되지 않은 것으로 처리합니다 (공평하게 말하자면 수행하려는 작업입니다). 0이 될 것으로 예상되는 경우 수동으로 0으로 설정하십시오.