Pregunta

He creado una clase simple de administrador de búfer para usar con sockets asíncronos. Esto protegerá contra la fragmentación de la memoria y mejorará el rendimiento. ¿Alguna sugerencia para nuevas mejoras u otros enfoques?

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;
    }
}
¿Fue útil?

Solución

Editar :

La respuesta original a continuación aborda un problema de construcción de código de acoplamiento demasiado apretado. Sin embargo, considerando la solución como un todo, evitaría usar solo un búfer grande y entregar trozos de esta manera. Expones tu código a un desbordamiento de búfer (y lo llamaremos búfer " underrun " cuestiones). En cambio, administraría una matriz de matrices de bytes, cada una de las cuales es un búfer discreto. El desplazamiento entregado siempre es 0 y el tamaño es siempre la longitud del búfer. Se detectará cualquier código incorrecto que intente leer / escribir partes más allá de los límites.

Respuesta original

Ha acoplado la clase a SocketAsyncEventArgs donde, de hecho, todo lo que necesita es una función para asignar el búfer, cambie SetBuffer a: -

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);
}

Ahora puedes llamar desde código de consumo algo como esto: -

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

No estoy seguro de que la inferencia de tipos sea lo suficientemente inteligente como para resolver los tipos de buf, offset, size en este caso. Si no, tendrá que colocar los tipos en la lista de argumentos: -

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

Sin embargo, ahora su clase puede usarse para asignar un búfer para todo tipo de requisitos que también usan el patrón de byte [], int, int, que es muy común.

Por supuesto, necesita desacoplar la operación gratuita pero eso es: -

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

Esto requiere que llame a SetBuffer en EventArgs para consumir código en el caso de SocketAsyncEventArgs . Si le preocupa que este enfoque reduzca la atomicidad de liberar el búfer y eliminarlo del uso de los sockets, subclase de este administrador de búfer ajustado e incluya el código específico de SocketAsyncEventArgs en la subclase.

Otros consejos

He creado una nueva clase con un enfoque completamente diferente.

Tengo una clase de servidor que recibe matrices de bytes. Luego invocará a diferentes delegados entregándoles los objetos de búfer para que otras clases puedan procesarlos. Cuando esas clases terminan, necesitan una forma de empujar los búferes de vuelta a la pila.

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;
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top