У меня есть класс, который упаковывает неуправляемое выделение в массив.Вы можете увидеть источник здесь, на Github , но вот его основная суть:
public unsafe class ArrayReference<T> : Reference, IArrayReference<T>
where T : unmanaged
{
private T* typedPointer_;
public T this[ int index ]
{
[MethodImpl( MethodImplOptions.AggressiveInlining )]
get => typedPointer_[ index ];
[MethodImpl( MethodImplOptions.AggressiveInlining )]
set => typedPointer_[ index ] = value;
}
}
Это довольно просто, а для операций чтения он обеспечивает высокую производительность (измеряется с помощью BenchmarkDotNet).):
Array size: 128
Managed byte[] ranged-for get: 69.8046 ns
ArrayReference ranged-for get: 66.7340 ns
Managed byte[] ranged-for set: 66.1855 ns
ArrayReference ranged-for set: 68.4863 ns
И код теста:
[GlobalSetup]
public void Setup()
{
median = AllocationSize / 2;
alloc_ = new Allocation( AllocationSize );
array_ = new ArrayReference<byte>( alloc_.Address, AllocationSize );
managedArray_ = new byte[ AllocationSize ];
}
[Benchmark]
public void ManagedArray_ranged_for_get()
{
var counter = 0;
for ( var i = 0; i < AllocationSize; i++ )
counter += managedArray_[ i ];
}
[Benchmark]
public void ArrayReference_ranged_for_get()
{
var counter = 0;
for ( var i = 0; i < AllocationSize; i++ )
counter += array_[ i ];
}
[Benchmark]
public void ManagedArray_ranged_for_set()
{
for ( var i = 0; i < AllocationSize; i++ )
managedArray_[ i ] = ( byte ) i;
}
[Benchmark]
public void ArrayReference_ranged_for_set()
{
for ( var i = 0; i < AllocationSize; i++ )
array_[ i ] = ( byte ) i;
}
Как видите, чтение с ArrayReference
немного быстрее, поскольку оно не выполняет проверку диапазона и егоимеет прямой доступ к указателю массива.Однако запись медленнее в ArrayReference
, чем в управляемом byte[]
массиве , и похоже, что проблема заключается в том, что установщик не является встроенным.
JIT x86для управляемого байта [] set:
managedArray_[ median ] = 0;
00007FFA237A216C mov rax,qword ptr [rbp+10h]
00007FFA237A2170 mov rax,qword ptr [rax+18h]
00007FFA237A2174 mov rdx,qword ptr [rbp+10h]
00007FFA237A2178 mov edx,dword ptr [rdx+24h]
00007FFA237A217B cmp rdx,qword ptr [rax+8]
00007FFA237A217F jb 00007FFA237A2186
00007FFA237A2181 call 00007FFA833DF110
00007FFA237A2186 lea rax,[rax+rdx+10h]
00007FFA237A218B mov byte ptr [rax],0
JIT x86 для ArrayReference :: set:
typedPointer_[ index ] = value;
00007FFA237A20BC mov rcx,qword ptr [rbp+10h]
00007FFA237A20C0 mov rcx,qword ptr [rcx+10h]
00007FFA237A20C4 mov rdx,qword ptr [rbp+10h]
00007FFA237A20C8 mov edx,dword ptr [rdx+24h]
00007FFA237A20CB xor r8d,r8d
00007FFA237A20CE cmp dword ptr [rcx],ecx
00007FFA237A20D0 call 00007FFA237A1738
->
00007FFA237A20F0 push rbp
00007FFA237A20F1 sub rsp,20h
00007FFA237A20F5 lea rbp,[rsp+20h]
00007FFA237A20FA mov qword ptr [rbp+10h],rcx
00007FFA237A20FE mov dword ptr [rbp+18h],edx
00007FFA237A2101 mov dword ptr [rbp+20h],r8d
00007FFA237A2105 cmp dword ptr [7FFA23688310h],0
00007FFA237A210C je 00007FFA237A2113
00007FFA237A210E call 00007FFA833DD3E0
00007FFA237A2113 mov rax,qword ptr [rbp+10h]
00007FFA237A2117 mov rax,qword ptr [rax+20h]
00007FFA237A211B mov edx,dword ptr [rbp+18h]
00007FFA237A211E movsxd rdx,edx
00007FFA237A2121 mov ecx,1
00007FFA237A2126 movsxd rcx,ecx
00007FFA237A2129 imul rdx,rcx
00007FFA237A212D mov ecx,dword ptr [rbp+20h]
00007FFA237A2130 mov byte ptr [rax+rdx],cl
Я не понимаю, почему это не встраивается.Он делает то же самое, что и управляемый массив, за исключением указателя на неуправляемую память.Нарушает ли это одно из встроенных правил CLR или, возможно, оно не является встроенным, потому что T
является общим, хотя и ограниченным?
Windows 10 Pro, 64-разрядная версия .Net Core 2.2, Release Mode 64Бит РюДЖИТ