[英]How to write to file with a pointer (byte*) to an array of byte
我為移動操作系統保存了太多文件大文件,所以我假設我應該使用byte*
來保存 memory 和分配
示例代碼
private unsafe static void WriteMesh(ref MeshInfo mesh, BinaryWriter bw)
{
var size = UnsafeUtility.SizeOf<float3>() * mesh.vertices.Length;
byte* pByte = (byte*)mesh.vertices.GetUnsafePtr();
bw.Write(pByte); // Obviously this wont work
}
我知道我可以用Span<T>
做到這一點,但我在 Unity 中不支持它。 還有其他方法嗎?
忽略任何其他問題(概念或其他)。 有幾種方法可以做到這一點。
復雜的例子接踵而至
如果您可以使用Span<T>
可以采用指針和長度,然后使用FileStream.Write(ReadOnlySpan<Byte>)
重載
將只讀范圍中的字節序列寫入當前文件 stream 並將當前 position 在此文件 stream 中前進寫入的字節數。
var bytes = new byte[] {1,2,3};
var size = bytes.Length;
using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);
fixed (byte* p = bytes)
{
var span = new Span<byte>(p, size);
fs.Write(span);
}
或者,只是使用BinaryWriter.Write
並寫入每個字節,這是一個̶̶l̶i̶t̶t̶l̶e̶... 效率極低
將有符號字節寫入當前 stream 並將 stream position 前進一個字節。
var bytes = new byte[] {1, 2, 3};
var size = bytes.Length;
using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);
using var bw = new BinaryWriter(fs);
fixed (byte* p = bytes)
for (int i = 0; i < size; i++)
bw.Write(*p);
或者,以分配為代價,只需Buffer.MemoryCopy
到新數組並Write
復制一塊 memory。
var bytes = new byte[] {1,2,3};
var size = bytes.Length;
using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);
var temp = new byte[size];
fixed (byte* pOld = bytes,pNew = temp)
{
Buffer.MemoryCopy(pOld,pNew,size,size);
fs.Write(temp,0,size);
}
或者,擴展數組復制方法,您可以使用ArrayPool<Byte>
來減少分配,反過來對您的LOH會更好(如果適用)
提供一個資源池,可以重用 T[] 類型的實例。
private static readonly ArrayPool<byte> _pool = ArrayPool<byte>.Shared;
...
var size = bytes.Length;
using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);
var temp = _pool.Rent(size);
try
{
fixed (byte* pOld = bytes, pNew = temp)
{
Buffer.MemoryCopy(pOld, pNew, size, size);
fs.Write(temp, 0, size);
}
}
finally
{
_pool.Return(temp);
}
或者您可以使用UnmanagedMemoryStream
提供從托管代碼對 memory 的非托管塊的訪問。
重要的
此 API 不符合 CLS。
var bytes = new byte[] {1,2,3};
var size = bytes.Length;
using var fs = new FileStream(@"SomeAwesomeFileNamedBob.dat", FileMode.Create);
fixed (byte* p = bytes)
{
using var us = new UnmanagedMemoryStream(p,size);
us.CopyTo(fs);
}
BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.630 (2004/?/20H1)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.100
[Host] : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT [AttachedDebugger]
.NET Core 5.0 : .NET Core 5.0.0 (CoreCLR 5.0.20.51904, CoreFX 5.0.20.51904), X64 RyuJIT
Job=.NET Core 5.0 Runtime=.NET Core 5.0
| Method | _size | Mean | Error | StdDev | Median |
|---------------- |------- |-------------:|-------------:|-------------:|-------------:|
| Span | 1000 | 122.4 ns | 2.46 ns | 2.42 ns | 122.7 ns |
| Single | 1000 | 5,548.3 ns | 82.61 ns | 73.23 ns | 5,561.8 ns |
| NewArray | 1000 | 230.4 ns | 4.64 ns | 10.56 ns | 227.4 ns |
| ArrayPool | 1000 | 185.6 ns | 3.74 ns | 4.60 ns | 186.1 ns |
| UnmanagedStream | 1000 | 249.8 ns | 4.89 ns | 8.69 ns | 247.5 ns |
|---------------- |------- |-------------:|-------------:|-------------:|-------------:|
| Span | 10000 | 1,012.9 ns | 20.06 ns | 44.87 ns | 1,007.0 ns |
| Single | 10000 | 56,143.2 ns | 980.01 ns | 1,436.48 ns | 56,087.6 ns |
| NewArray | 10000 | 2,086.1 ns | 43.89 ns | 127.34 ns | 2,048.9 ns |
| ArrayPool | 10000 | 1,277.2 ns | 24.38 ns | 50.88 ns | 1,272.3 ns |
| UnmanagedStream | 10000 | 1,267.8 ns | 24.52 ns | 28.24 ns | 1,260.9 ns |
|---------------- |------- |-------------:|-------------:|-------------:|-------------:|
| Span | 100000 | 56,843.0 ns | 1,107.92 ns | 1,137.75 ns | 56,587.5 ns |
| Single | 100000 | 601,186.9 ns | 11,991.48 ns | 17,576.95 ns | 598,002.9 ns |
| NewArray | 100000 | 111,234.1 ns | 1,296.51 ns | 1,012.23 ns | 111,268.3 ns |
| ArrayPool | 100000 | 59,183.1 ns | 278.01 ns | 232.15 ns | 59,141.8 ns |
| UnmanagedStream | 100000 | 58,539.6 ns | 941.79 ns | 834.87 ns | 58,176.1 ns |
設置
[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public unsafe class DumbTest
{
[Params(1000, 10000, 100000)] public int _size;
private byte* _p;
private GCHandle _handle;
private readonly ArrayPool<byte> _pool = ArrayPool<byte>.Shared;
[GlobalSetup]
public void Setup()
{
var bytes = new byte[_size];
new Random(42).NextBytes(bytes);
_handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
_p = (byte*) _handle.AddrOfPinnedObject();
}
[GlobalCleanup]
public void Cleanup() => _handle.Free();
[Benchmark]
public void Span()
{
using var ms = new MemoryStream();
var span = new Span<byte>(_p, _size);
ms.Write(span);
}
[Benchmark]
public void Single()
{
using var ms = new MemoryStream();
using var bw = new BinaryWriter(ms);
for (var i = 0; i < _size; i++)
bw.Write(*_p);
}
[Benchmark]
public void NewArray()
{
using var ms = new MemoryStream();
var temp = new byte[_size];
fixed (byte* pNew = temp)
{
Buffer.MemoryCopy(_p, pNew, _size, _size);
ms.Write(temp, 0, _size);
}
}
[Benchmark]
public void ArrayPool()
{
using var ms = new MemoryStream();
var temp = _pool.Rent(_size);
try
{
fixed (byte* pNew = temp)
{
Buffer.MemoryCopy(_p,pNew,_size,_size);
ms.Write(temp,0,_size);
}
}
finally
{
_pool.Return(temp);
}
}
[Benchmark]
public void UnmanagedStream()
{
using var ms = new MemoryStream();
using var us = new UnmanagedMemoryStream(_p, _size);
us.CopyTo(ms);
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.