Сообщение Re: Unsafe mem mapping от 28.02.2024 10:59
Изменено 28.02.2024 14:19 Sinclair
Re: Unsafe mem mapping
Здравствуйте, Sаныч, Вы писали:
S>Появился ли в последний версия языка что-то для сабжа более правильный с т.з. дизайна кода? Сейчас делаю так
S>
Этот подход — небезопасный и медленный. Вот эта строчка: return *(NativeStruct*)map; tt] означает "скопируй мне 8 байт из _readBuffer в стек", и потом ещё "скопируй мне эти 8 байт туда, где будет выполнен возврат". Детали зависят от множества вещей вроде размера NativeStruct и конкретной версии JIT.
S>Читал про Span<T> но не понял как его применить для задачи, если он вообще применим для такого.
Чтобы понять, можно ли применить Span<T> нужно подняться на пару уровней выше над [tt]*(NativeStruct*)map. Что именно вы делаете потом?
Span нужен для того, чтобы, к примеру, с нулевым оверхедом превратить "массив байт" в "массив NativeStruct".
Вот, например, такой код вызывает полноценное копирование 8 байт минимум дважды (а, может быть, и трижды):
Это выглядит расточительным, если вам нужны только 4 байта Field2.
А вот такой код — только однократное копирование 4х байт (и без unsafe):
Если в _readBuffer на самом деле лежит целый массив NativeStruct, то Span позволит его отмаршалить с O(1) оверхедом:
После этого вы сможете, к примеру, итерироваться по NativeStructs() без выделения памяти.
Но всё это зависит от того, что именно вы делаете.
S>Появился ли в последний версия языка что-то для сабжа более правильный с т.з. дизайна кода? Сейчас делаю так
S>
S>[StructLayout(LayoutKind.Explicit)]
S>struct NativeStruct
S>{
S> [FieldOffset(0)]
S> public int Field1;
S> [FieldOffset(4)]
S> public uint Field2;
S> // дальше
S>}
S>fixed (byte* map = &_readBuffer[0])
S>{
S> return *(NativeStruct*)map;
S>}
S>
Этот подход — небезопасный и медленный. Вот эта строчка: return *(NativeStruct*)map; tt] означает "скопируй мне 8 байт из _readBuffer в стек", и потом ещё "скопируй мне эти 8 байт туда, где будет выполнен возврат". Детали зависят от множества вещей вроде размера NativeStruct и конкретной версии JIT.
S>Читал про Span<T> но не понял как его применить для задачи, если он вообще применим для такого.
Чтобы понять, можно ли применить Span<T> нужно подняться на пару уровней выше над [tt]*(NativeStruct*)map. Что именно вы делаете потом?
Span нужен для того, чтобы, к примеру, с нулевым оверхедом превратить "массив байт" в "массив NativeStruct".
Вот, например, такой код вызывает полноценное копирование 8 байт минимум дважды (а, может быть, и трижды):
private byte[] _readBuffer = new byte[80];
public void Test1()
{
var t = Foo().Field2;
Console.WriteLine(t);
}
public unsafe NativeStruct Foo()
{
fixed (byte* map = &_readBuffer[0])
{
return *(NativeStruct*)map;
}
}
Это выглядит расточительным, если вам нужны только 4 байта Field2.
А вот такой код — только однократное копирование 4х байт (и без unsafe):
public void Test2()
{
var t = Bar().Field2;
Console.WriteLine(t);
}
public ref NativeStruct Bar()
{
return ref Marshal.AsRef<NativeStruct>(_readBuffer.AsSpan());
}
private byte[] _readBuffer = new byte[80];
Если в _readBuffer на самом деле лежит целый массив NativeStruct, то Span позволит его отмаршалить с O(1) оверхедом:
public Span<NativeStruct> NativeStructs()
=> Marshal.Cast<byte, NativeStruct>(_readBuffer.AsSpan());
После этого вы сможете, к примеру, итерироваться по NativeStructs() без выделения памяти.
Но всё это зависит от того, что именно вы делаете.
Re: Unsafe mem mapping
Здравствуйте, Sаныч, Вы писали:
S>Появился ли в последний версия языка что-то для сабжа более правильный с т.з. дизайна кода? Сейчас делаю так
S>
Этот подход — небезопасный и медленный. Вот эта строчка: return *(NativeStruct*)map; означает "скопируй мне 8 байт из _readBuffer в стек", и потом ещё "скопируй мне эти 8 байт туда, где будет выполнен возврат". Детали зависят от множества вещей вроде размера NativeStruct и конкретной версии JIT.
S>Читал про Span<T> но не понял как его применить для задачи, если он вообще применим для такого.
Чтобы понять, можно ли применить Span<T> нужно подняться на пару уровней выше над *(NativeStruct*)map. Что именно вы делаете потом?
Span нужен для того, чтобы, к примеру, с нулевым оверхедом превратить "массив байт" в "массив NativeStruct".
Вот, например, такой код вызывает полноценное копирование 8 байт минимум дважды (а, может быть, и трижды):
Это выглядит расточительным, если вам нужны только 4 байта Field2.
А вот такой код — только однократное копирование 4х байт (и без unsafe):
Если в _readBuffer на самом деле лежит целый массив NativeStruct, то Span позволит его отмаршалить с O(1) оверхедом:
После этого вы сможете, к примеру, итерироваться по NativeStructs() без выделения памяти.
Но всё это зависит от того, что именно вы делаете.
S>Появился ли в последний версия языка что-то для сабжа более правильный с т.з. дизайна кода? Сейчас делаю так
S>
S>[StructLayout(LayoutKind.Explicit)]
S>struct NativeStruct
S>{
S> [FieldOffset(0)]
S> public int Field1;
S> [FieldOffset(4)]
S> public uint Field2;
S> // дальше
S>}
S>fixed (byte* map = &_readBuffer[0])
S>{
S> return *(NativeStruct*)map;
S>}
S>
Этот подход — небезопасный и медленный. Вот эта строчка: return *(NativeStruct*)map; означает "скопируй мне 8 байт из _readBuffer в стек", и потом ещё "скопируй мне эти 8 байт туда, где будет выполнен возврат". Детали зависят от множества вещей вроде размера NativeStruct и конкретной версии JIT.
S>Читал про Span<T> но не понял как его применить для задачи, если он вообще применим для такого.
Чтобы понять, можно ли применить Span<T> нужно подняться на пару уровней выше над *(NativeStruct*)map. Что именно вы делаете потом?
Span нужен для того, чтобы, к примеру, с нулевым оверхедом превратить "массив байт" в "массив NativeStruct".
Вот, например, такой код вызывает полноценное копирование 8 байт минимум дважды (а, может быть, и трижды):
private byte[] _readBuffer = new byte[80];
public void Test1()
{
var t = Foo().Field2;
Console.WriteLine(t);
}
public unsafe NativeStruct Foo()
{
fixed (byte* map = &_readBuffer[0])
{
return *(NativeStruct*)map;
}
}
Это выглядит расточительным, если вам нужны только 4 байта Field2.
А вот такой код — только однократное копирование 4х байт (и без unsafe):
public void Test2()
{
var t = Bar().Field2;
Console.WriteLine(t);
}
public ref NativeStruct Bar()
{
return ref Marshal.AsRef<NativeStruct>(_readBuffer.AsSpan());
}
private byte[] _readBuffer = new byte[80];
Если в _readBuffer на самом деле лежит целый массив NativeStruct, то Span позволит его отмаршалить с O(1) оверхедом:
public Span<NativeStruct> NativeStructs()
=> Marshal.Cast<byte, NativeStruct>(_readBuffer.AsSpan());
После этого вы сможете, к примеру, итерироваться по NativeStructs() без выделения памяти.
Но всё это зависит от того, что именно вы делаете.