4

Struct1の配列をStruct2(同じバイナリ表現) の配列に可能な限り最速の方法でコピーしようとしています。Struct1[]と の間で変換する共用体を定義しましたStruct2[]が、Array.Copy を呼び出すと、配列が間違った型であるという例外が発生します。どうすればそれを回避できますか? Buffer.BlockCopy はプリミティブ型のみを受け入れます。コードは次のとおりです。

[StructLayout(LayoutKind.Explicit)]
public struct Struct12Converter
{
    [FieldOffset(0)]
    public Struct1[] S1Array;
    [FieldOffset(0)]
    public Struct2[] S2Array;
}

public void ConversionTest()
{
    var s1Array = new{new Struct1()}
    var converter = new Struct12Converter{S1Array = s1Array};
    var s2Array = new Struct2[1];
    Array.Copy(converter.S2Array,0,s2Array,0,1) //throws here
    //if you check with the debugger, it says converter.S2Array is a Struct1[], 
    //although the compiler lets you use it like a Struct2[]
    //this has me baffled as well.
}

詳細を説明すると、変更可能な構造体を操作してそのフィールドの値を変更すると、同じ不変の構造体を使用して常に作業する場合と比較して、パフォーマンス特性が異なるかどうかを実験したかったのです。似ているはずですが、測定する価値があると思いました。基礎となるアプリケーションは、私が現在使用している低レイテンシ ソケット ライブラリであり、ArraySegment<byte>ベースのソケット API です。SocketAsyncEventArgsAPIで、BufferListプロパティを設定すると、私の「実験」が失敗する場所である配列コピーがトリガーされることがあります(以前と同じ方法で変換できない配列があるため、比較が無意味になります)MutableArraySegmentArraySegment[]

4

4 に答える 4

0

Struct1[]a を aとして安全に扱うことによって、型システムを壊したことに気づいていますStruct2[]か? これにより、CLR が未定義の状態になります。Struct1[]タイプの変数が実際に のインスタンスを指していると仮定できますStruct1[]。現在、ほとんどの奇妙な動作を確認できます。(これはセキュリティ上の問題ではありません。このコードは検証可能ではなく、完全な信頼が必要です。)

つまり、配列の内容は変換されていませんが、変換されたオブジェクト参照が取得されています。

通常、blittable オブジェクトの配列のコピーは、 を使用した最速の方法で行われますmemcpy。手動のコピー ループはそれと同等ですが、JIT を信頼してmemcpy. 現在のバージョンでは、JIT は基本的な最適化のみを行います。

于 2014-06-06T11:44:56.217 に答える
0

このコードは意図的に安全ではありません (あなたがやりたいことは安全ではないので、CLR/JIT はパフォーマンス上の理由から構造体を並べ替えることができます)。

また、フレームワークのバージョンによって MemCpy の署名が変わる可能性があることにも注意してください (結局は内部的なものです)。

パフォーマンス上の理由から、デリゲートを適切にキャッシュする必要があります

この質問からのアイデアはこちら

    unsafe delegate void MemCpyImpl(byte* src, byte* dest, int len);

    static MemCpyImpl memcpyimpl;

    public unsafe static void Copy(void* src, void* dst, int count)
    {
        byte* source = (byte*)src;
        byte* dest = (byte*)dst;
        memcpyimpl(source, dest, count);
    }

次に、配列を強制的にバイト配列にします(実際には無効*ですが、詳細は気にしないでください)

    public static void ConversionTest()
    {
        var bufferType = typeof(Buffer);

        unsafe
        {
            var paramList = new Type[3] { typeof(byte*), typeof(byte*), typeof(int) };
            var memcpyimplMethod = bufferType.GetMethod("Memcpy", BindingFlags.Static | BindingFlags.NonPublic, null, paramList, null);

            memcpyimpl = (MemCpyImpl)Delegate.CreateDelegate(typeof(MemCpyImpl), memcpyimplMethod);
        }

        Struct1[] s1Array = { new Struct1() { value = 123456789 } };
        var converter = new Struct12Converter { S1Array = s1Array };
        var s2Array = new Struct2[1];
        unsafe
        {
            fixed (void* bad = s2Array)
            {
                fixed (void* idea = converter.S2Array)
                {
                    Copy(bad, idea, 4);
                }
            }
        }
    }
于 2014-06-06T13:40:27.717 に答える