1

非同期ソケットで使用する単純なバッファーマネージャークラスを作成しました。これにより、メモリの断片化から保護され、パフォーマンスが向上します。さらなる改善または他のアプローチのための提案はありますか?

public class BufferManager
{
    private int[] free;
    private byte[] buffer;
    private readonly int blocksize;

    public BufferManager(int count, int blocksize)
    {
        buffer = new byte[count * blocksize];
        free = new int[count];
        this.blocksize = blocksize;

        for (int i = 0; i < count; i++)
            free[i] = 1;
    }

    public void SetBuffer(SocketAsyncEventArgs args)
    {
        for (int i = 0; i < free.Length; i++)
        {
            if (1 == Interlocked.CompareExchange(ref free[i], 0, 1))
            {
                args.SetBuffer(buffer, i * blocksize, blocksize);
                return;
            }
        }
        args.SetBuffer(new byte[blocksize], 0, blocksize);
    }

    public void FreeBuffer(SocketAsyncEventArgs args)
    {
        int offset = args.Offset;
        byte[] buff = args.Buffer;

        args.SetBuffer(null, 0, 0);

        if (buffer == buff)
            free[offset / blocksize] = 1;
    }
}
4

2 に答える 2

0

編集

以下の元の回答は、過度に緊密な結合のコード構築の問題に対処しています。ただし、ソリューション全体を考えると、大きなバッファーを 1 つだけ使用して、そのスライスをこのように渡すことは避けます。コードをバッファ オーバーランにさらします (これをバッファの「アンダーラン」問題と呼ぶことにします)。代わりに、それぞれが個別のバッファーであるバイト配列の配列を管理します。渡されるオフセットは常に 0 で、サイズは常にバッファーの長さです。境界を越えてパーツを読み書きしようとする悪いコードはすべてキャッチされます。

元の答え

クラスを SocketAsyncEventArgs に結合しました。実際に必要なのは、バッファーを割り当てる関数だけです。 SetBuffer を次のように変更します。

public void SetBuffer(Action<byte[], int, int> fnSet)
{
    for (int i = 0; i < free.Length; i++)
    {
        if (1 == Interlocked.CompareExchange(ref free[i], 0, 1))
        {
            fnSet(buffer, i * blocksize, blocksize);
            return;
        }
    }
    fnSet(new byte[blocksize], 0, blocksize);
}

これで、次のようなコードを消費して呼び出すことができます:-

myMgr.SetBuffer((buf, offset, size) => myArgs.SetBuffer(buf, offset, size));

この場合、型推論が型を解決するのに十分賢いかどうかはわかりませんbuf, offset, size。そうでない場合は、引数リストに型を配置する必要があります:-

myMgr.SetBuffer((byte[] buf, int offset, int size) => myArgs.SetBuffer(buf, offset, size));

ただし、現在、クラスを使用して、非常に一般的な byte[]、int、int パターンも使用するあらゆる種類の要件にバッファーを割り当てることができます。

もちろん、無料の操作を切り離す必要がありますが、それは次のとおりです。

public void FreeBuffer(byte[] buff, int offset)
{
    if (buffer == buff)
        free[offset / blocksize] = 1;
}

この場合、コードを使用する際に EventArgs で SetBuffer を呼び出す必要がありますSocketAsyncEventArgs。このアプローチにより、バッファーを解放してソケットの使用から削除する原子性が低下することが懸念される場合は、この調整されたバッファー マネージャーをSocketAsyncEventArgsサブクラス化し、サブクラスに特定のコードを含めます。

于 2009-09-10T08:18:53.310 に答える
0

まったく異なるアプローチで新しいクラスを作成しました。

バイト配列を受け取るサーバー クラスがあります。次に、別のデリゲートを呼び出してバッファ オブジェクトを渡し、他のクラスがそれらを処理できるようにします。これらのクラスが完了すると、バッファをスタックにプッシュする方法が必要になります。

public class SafeBuffer
{
    private static Stack bufferStack;
    private static byte[][] buffers;

    private byte[] buffer;
    private int offset, lenght;

    private SafeBuffer(byte[] buffer)
    {
        this.buffer = buffer;
        offset = 0;
        lenght = buffer.Length;
    }

    public static void Init(int count, int blocksize)
    {
        bufferStack = Stack.Synchronized(new Stack());
        buffers = new byte[count][];

        for (int i = 0; i < buffers.Length; i++)
            buffers[i] = new byte[blocksize];

        for (int i = 0; i < buffers.Length; i++)
            bufferStack.Push(new SafeBuffer(buffers[i]));
    }

    public static SafeBuffer Get()
    {
        return (SafeBuffer)bufferStack.Pop();
    }

    public void Close()
    {
        bufferStack.Push(this);
    }

    public byte[] Buffer
    {
        get
        {
            return buffer;
        }
    }

    public int Offset
    {
        get
        {
            return offset;
        }
        set
        {
            offset = value;
        }
    }

    public int Lenght
    {
        get
        {
            return buffer.Length;
        }
    }
}
于 2009-09-11T09:37:26.883 に答える