純粋な C# アプローチ
したがって、いくつかのオプションがあります。最も簡単なのは、構造体の安全でないコンテキストで new/delete を使用することです。2 つ目は、組み込みのマーシャリング サービスを使用してアンマネージ メモリを処理することです (このコードは以下に表示されています)。ただし、これらは両方とも構造体を処理します (ただし、後者の方法は必要なものに非常に近いと思います)。私のコードには、構造全体に固執し、参照に IntPtrs を使用する必要があるという制限があります (ChunkAllocator.ConvertPointerToStructure を使用してデータを取得し、ChunkAllocator.StoreStructure を使用して変更されたデータを保存します)。これは明らかに面倒なので、私のアプローチを使用する場合は、本当にパフォーマンスが必要です。ただし、値型のみを扱う場合は、このアプローチで十分です。
回り道: CLR のクラス
クラスには、割り当てられたメモリに 8 バイトの「プレフィックス」があります。4 バイトはマルチスレッドの同期インデックス用で、4 バイトはそれらのタイプ (基本的には仮想メソッド テーブルとランタイム リフレクション) を識別するためのものです。これらは CLR 固有であり、実行時に同期インデックスが変更される可能性があるため、これによりアンマネージ メモリの処理が難しくなります。実行時オブジェクト作成の詳細についてはこちらを、参照型のメモリ レイアウトの概要についてはこちらを参照してください。より詳細な説明については、C# による CLRも参照してください。
警告
いつものように、イエス/ノーのような単純なことはめったにありません。参照型の実際の複雑さは、ガベージ コレクション中にガベージ コレクターが割り当てられたメモリを圧縮する方法に関係しています。ガベージ コレクションが発生しないこと、または問題のデータに影響を与えないことを何らかの方法で確認できる場合 ( fixed キーワードを参照)、任意のポインターをオブジェクト参照に変えることができます (ポインターを 8 バイトだけオフセットし、次に、そのデータを同じフィールドとメモリ レイアウトを持つ構造体として解釈します (おそらく確実にStructLayoutAttributeを使用します)。非仮想メソッドを試して、それらが機能するかどうかを確認します。それらは(特に構造体に配置する場合)必要がありますが、仮想メソッドテーブルは破棄する必要があるため、仮想メソッドは使用できません。
モルドールに足を踏み入れるだけではない
簡単に言えば、これはマネージド参照型 (クラス) をアンマネージド メモリに割り当てることができないことを意味します。struct
C++ でマネージ参照型を使用することもできますが、それらはガベージ コレクションの対象となります...そして、プロセスとコードは、ベースのアプローチよりも面倒です。それは私たちをどこに残しますか?もちろん、出発点に戻ります。
秘密の方法があります
自分たちでShelob's Lair のメモリ割り当てに立ち向かうことができました。残念ながら、私はそこまで詳しくないので、ここで私たちの道は分かれなければなりません。リンクを 1 つまたは2つ、実際には3 つまたは4 つ提供します。これはかなり複雑で、次のような疑問が生じます。他に試すことができる最適化はありますか? キャッシュの一貫性と優れたアルゴリズムは、パフォーマンスが重要なコードに対する P/Invoke の賢明な適用と同様に、1 つのアプローチです。前述の構造体のみのメモリ割り当てを主要なメソッド/クラスに適用することもできます。
頑張ってください。より優れた代替案が見つかったらお知らせください。
付録: ソースコード
ChunkAllocator.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace MemAllocLib
{
public sealed class ChunkAllocator : IDisposable
{
IntPtr m_chunkStart;
int m_offset;//offset from already allocated memory
readonly int m_size;
public ChunkAllocator(int memorySize = 1024)
{
if (memorySize < 1)
throw new ArgumentOutOfRangeException("memorySize must be positive");
m_size = memorySize;
m_chunkStart = Marshal.AllocHGlobal(memorySize);
}
~ChunkAllocator()
{
Dispose();
}
public IntPtr Allocate<T>() where T : struct
{
int reqBytes = Marshal.SizeOf(typeof(T));//not highly performant
return Allocate<T>(reqBytes);
}
public IntPtr Allocate<T>(int reqBytes) where T : struct
{
if (m_chunkStart == IntPtr.Zero)
throw new ObjectDisposedException("ChunkAllocator");
if (m_offset + reqBytes > m_size)
throw new OutOfMemoryException("Too many bytes allocated: " + reqBytes + " needed, but only " + (m_size - m_offset) + " bytes available");
T created = default(T);
Marshal.StructureToPtr(created, m_chunkStart + m_offset, false);
m_offset += reqBytes;
return m_chunkStart + (m_offset - reqBytes);
}
public void Dispose()
{
if (m_chunkStart != IntPtr.Zero)
{
Marshal.FreeHGlobal(m_chunkStart);
m_offset = 0;
m_chunkStart = IntPtr.Zero;
}
}
public void ReleaseAllMemory()
{
m_offset = 0;
}
public int AllocatedMemory
{
get { return m_offset; }
}
public int AvailableMemory
{
get { return m_size - m_offset; }
}
public int TotalMemory
{
get { return m_size; }
}
public static T ConvertPointerToStruct<T>(IntPtr ptr) where T : struct
{
return (T)Marshal.PtrToStructure(ptr, typeof(T));
}
public static void StoreStructure<T>(IntPtr ptr, T data) where T : struct
{
Marshal.StructureToPtr(data, ptr, false);
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MemoryAllocation
{
class Program
{
static void Main(string[] args)
{
using (MemAllocLib.ChunkAllocator chunk = new MemAllocLib.ChunkAllocator())
{
Console.WriteLine(">> Simple data test");
SimpleDataTest(chunk);
Console.WriteLine();
Console.WriteLine(">> Complex data test");
ComplexDataTest(chunk);
}
Console.ReadLine();
}
private static void SimpleDataTest(MemAllocLib.ChunkAllocator chunk)
{
IntPtr ptr = chunk.Allocate<System.Int32>();
Console.WriteLine(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Int32>(ptr));
System.Diagnostics.Debug.Assert(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Int32>(ptr) == 0, "Data not initialized properly");
System.Diagnostics.Debug.Assert(chunk.AllocatedMemory == sizeof(Int32), "Data not allocated properly");
int data = MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Int32>(ptr);
data = 10;
MemAllocLib.ChunkAllocator.StoreStructure(ptr, data);
Console.WriteLine(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Int32>(ptr));
System.Diagnostics.Debug.Assert(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Int32>(ptr) == 10, "Data not set properly");
Console.WriteLine("All tests passed");
}
private static void ComplexDataTest(MemAllocLib.ChunkAllocator chunk)
{
IntPtr ptr = chunk.Allocate<Person>();
Console.WriteLine(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Person>(ptr));
System.Diagnostics.Debug.Assert(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Person>(ptr).Age == 0, "Data age not initialized properly");
System.Diagnostics.Debug.Assert(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Person>(ptr).Name == null, "Data name not initialized properly");
System.Diagnostics.Debug.Assert(chunk.AllocatedMemory == System.Runtime.InteropServices.Marshal.SizeOf(typeof(Person)) + sizeof(Int32), "Data not allocated properly");
Person data = MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Person>(ptr);
data.Name = "Bob";
data.Age = 20;
MemAllocLib.ChunkAllocator.StoreStructure(ptr, data);
Console.WriteLine(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Person>(ptr));
System.Diagnostics.Debug.Assert(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Person>(ptr).Age == 20, "Data age not set properly");
System.Diagnostics.Debug.Assert(MemAllocLib.ChunkAllocator.ConvertPointerToStruct<Person>(ptr).Name == "Bob", "Data name not set properly");
Console.WriteLine("All tests passed");
}
struct Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
public override string ToString()
{
if (string.IsNullOrWhiteSpace(Name))
return "Age is " + Age;
return Name + " is " + Age + " years old";
}
}
}
}