문제

비동기 소켓과 함께 사용할 간단한 버퍼 관리자 클래스를 만들었습니다.이렇게 하면 메모리 조각화를 방지하고 성능이 향상됩니다.추가 개선이나 다른 접근 방식에 대한 제안이 있으십니까?

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;
    }
}
도움이 되었습니까?

해결책

편집하다:

아래의 Orignal 답변은 지나치게 긴밀한 커플 링의 코드 구성 문제를 다룹니다. 그러나 솔루션을 전체적으로 고려하면 하나의 큰 버퍼를 사용하고 이런 방식으로 슬라이스를 넘겨주지 않습니다. 코드를 버퍼 오버런에 노출시킵니다 (버퍼를 "Underrun"문제라고 부를 것임). 대신 나는 각각의 바이트 배열 배열을 각각 이산 버퍼입니다. 오프셋 전달은 항상 0이고 크기는 항상 버퍼의 길이입니다. 경계를 넘어 부분을 읽거나 쓰려고 시도하는 잘못된 코드는 잡히게됩니다.

원래 답변

당신은 클래스를 SocketAsynceventArg와 결합하여 실제로 필요한 모든 것이 버퍼를 할당하는 기능이 다음과 같이 설정하고 다음으로 설정했습니다.

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

그러나 이제 클래스를 사용하여 매우 일반적인 바이트 [], int, int 패턴을 사용하는 모든 방식의 요구 사항에 대한 버퍼를 할당 할 수 있습니다.

물론 무료 작업을 해제해야하지만 다음과 같습니다.

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

이를 위해서는 사건에서 코드를 소비하여 EventArgs에서 SetBuffer에 전화해야합니다. SocketAsyncEventArgs. 이 접근법이 버퍼를 제거하고 소켓 사용에서 제거하는 원자력을 감소 시키면이 조정 된 버퍼 관리자를 서브 클래스하고 포함시켜줍니다. SocketAsyncEventArgs 하위 클래스의 특정 코드.

다른 팁

저는 완전히 다른 접근 방식으로 새로운 클래스를 만들었습니다.

바이트 배열을 받는 서버 클래스가 있습니다.그런 다음 다른 클래스가 처리할 수 있도록 버퍼 개체를 전달하는 다른 대리자를 호출합니다.해당 클래스가 완료되면 버퍼를 스택으로 다시 푸시하는 방법이 필요합니다.

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;
        }
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top