10

C# でパーサー コンビネーターの最適化を試しています。シリアル化された形式がメモリ内形式と一致する場合に考えられる最適化の 1 つは、型のインスタンスまたは多くのインスタンスで解析されるデータの (安全でない) memcpy を実行することです。

最適化を適用できるかどうかを動的に判断するために、インメモリ形式がシリアル化された形式と一致するかどうかを判断するコードを記述したいと考えています。(明らかに、これは安全でない最適化であり、さまざまな微妙な理由で機能しない可能性があります。私は実験しているだけであり、本番コードでこれを使用する予定はありません。)

属性[StructLayout(LayoutKind.Sequential, Pack = 1)]を使用して、強制的にパディングを行わず、メモリ内の順序を宣言の順序と一致させます。私はリフレクションでその属性をチェックしますが、実際にはこれが確認するのは「パディングなし」です。フィールドの順序も必要です。(エラーが発生しやすいため、フィールドごとにFieldOffset属性を手動で指定する必要はありません。)

GetFieldsによって返されるフィールドの順序を使用できると想定しましたが、ドキュメントでは、順序が指定されていないことを明示的に示しています。

StructLayout 属性を使用してフィールドの順序を強制している場合、その順序を反映する方法はありますか?

editすべてのフィールドがblittableでなければならないという制限は問題ありません。

4

2 に答える 2

5

LayoutKind.Sequentialblittable 型で使用する場合、これは不要です

すべてのフィールドが blittable である限り、メモリ内の構造体フィールドの順序を調べるために、リフレクションやその他のメカニズムを使用する必要はありません。

with で宣言された構造体の blittable フィールドはLayoutKind.Sequential、フィールドが宣言された順序でメモリに格納されます。というLayoutKind.Sequentialことです!

このドキュメントから

blittable 型の場合、LayoutKind.Sequential はマネージド メモリ内のレイアウトとアンマネージド メモリ内のレイアウトの両方を制御します。blittable でない型の場合、クラスまたは構造体がアンマネージ コードにマーシャリングされるときにレイアウトを制御しますが、マネージ メモリ内のレイアウトは制御しません。

これは、各フィールドが使用しているパディングの量を示していないことに注意してください。それを見つけるには、以下を参照してください。

を使用する場合のフィールドの順序LayoutKind.Auto、または任意のレイアウトを使用する場合のフィールド オフセットを決定するには

安全でないコードを使用し、リフレクションを使用しない場合は、構造体フィールドのオフセットを見つけるのはかなり簡単です。

構造体の各フィールドのアドレスを取得し、構造体の先頭からのオフセットを計算するだけです。各フィールドのオフセットがわかれば、それらの順序 (およびそれらの間のパディング バイト) を計算できます。最後のフィールド (存在する場合) に使用されるパディング バイトを計算するには、 を使用して構造体の合計サイズも取得する必要がありますsizeof(StructType)

次の例は、32 ビットと 64 ビットで機能します。スタック上にあるため、構造体は既に修正されているため、キーワードを使用する必要がないことに注意してくださいfixed(使用しようとすると、コンパイル エラーが発生しますfixed)。

using System;
using System.Runtime.InteropServices;

namespace Demo
{
    [StructLayout(LayoutKind.Auto, Pack = 1)]

    public struct TestStruct
    {
        public int    I;
        public double D;
        public short  S;
        public byte   B;
        public long   L;
    }

    class Program
    {
        void run()
        {
            var t = new TestStruct();

            unsafe
            {
                IntPtr p  = new IntPtr(&t);
                IntPtr pI = new IntPtr(&t.I);
                IntPtr pD = new IntPtr(&t.D);
                IntPtr pS = new IntPtr(&t.S);
                IntPtr pB = new IntPtr(&t.B);
                IntPtr pL = new IntPtr(&t.L);

                Console.WriteLine("I offset = " + ptrDiff(p, pI));
                Console.WriteLine("D offset = " + ptrDiff(p, pD));
                Console.WriteLine("S offset = " + ptrDiff(p, pS));
                Console.WriteLine("B offset = " + ptrDiff(p, pB));
                Console.WriteLine("L offset = " + ptrDiff(p, pL));

                Console.WriteLine("Total struct size = " + sizeof(TestStruct));
            }
        }

        long ptrDiff(IntPtr p1, IntPtr p2)
        {
            return p2.ToInt64() - p1.ToInt64();
        }

        static void Main()
        {
            new Program().run();
        }
    }
}

使用時にフィールド オフセットを決定するにはLayoutKind.Sequential

構造体が使用する場合は、オフセットを直接取得するためにLayoutKind.Sequential使用できますが、これは次の場合には機能しませMarshal.OffsetOf()LayoutKind.Auto

foreach (var field in typeof(TestStruct).GetFields())
{
    var offset = Marshal.OffsetOf(typeof (TestStruct), field.Name);
    Console.WriteLine("Offset of " + field.Name + " = " + offset);
}

LayoutKind.Sequentialこれは、コードを必要とせず、はるかに短いため、使用している場合は明らかにより良い方法ですunsafe。また、フィールドの名前を事前に知る必要もありません。上で述べたように、メモリ内のフィールドの順序を決定する必要はありませんが、これは、使用されているパディングの量を調べる必要がある場合に役立ちます。

于 2013-07-07T08:46:56.783 に答える
3

順番やレイアウトの種類を知りたい方の参考に。たとえば、型に blittable でない型が含まれている場合です。

var fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
fields.SortByFieldOffset();

var isExplicit = typeof(T).IsExplicitLayout;
var isSequential = typeof(T).IsLayoutSequential;

私が書いた拡張メソッドを使用します。

    public static void SortByFieldOffset(this FieldInfo[] fields) {
        Array.Sort(fields, (a, b) => OffsetOf(a).CompareTo(OffsetOf(b)) );
    }

    private static int OffsetOf(FieldInfo field) {
        return Marshal.OffsetOf(field.DeclaringType, field.Name).ToInt32();
    }

MSDN には、IsLayoutSequentialに関する有用な情報が含まれています。

于 2014-04-30T12:24:13.783 に答える