2009-09-10 1 views
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; 
    } 
} 

답변

0

편집 다음 시킴으로 빨리 답은 아래에 지나치게 꽉 커플 링의 코드 건설 문제를 해결

. 그러나 전체적으로 솔루션을 고려할 때 하나의 대형 버퍼를 사용하지 않고이 방법으로 슬라이스를 넘겨주는 일은 피할 수 있습니다. 버퍼 오버런에 코드를 노출합니다 (버퍼 언더런 문제라고 부름). 대신 바이트 배열 배열을 개별 버퍼로 관리 할 것입니다. 넘겨진 오프셋은 항상 0이고 크기는 항상 버퍼의 길이입니다. 경계를 넘어서 부분을 읽고 쓰려고 시도하는 나쁜 코드가 발견됩니다. 이제

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

당신이 호출 할 수 있습니다 : -

당신은 실제로는 필요로하는 모든 버퍼를 할당하는 기능입니다 SocketAsyncEventArgs에 클래스를 결합했습니다

원래 대답하는 SetBuffer 변경 -

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 패턴을 사용하여 요구 사항의 모든 방식에 대한 버퍼를 할당하는 데 사용할 수 있습니다 이것은 매우 일반적입니다. 물론

당신은 무료로 작업을 분리 할 필요로하지만, 그게 전부 : -이 SocketAsyncEventArgs의 경우 코드를 소모에있는 EventArgs에 SetBuffer를 호출 할 필요

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

. 이 접근 방식이 버퍼를 해제하고 소켓에서 제거하는 것을 원한다면 하위 클래스는 조정 된 버퍼 관리자를 사용하고 SocketAsyncEventArgs 특정 코드를 하위 클래스에 포함시킵니다.

+0

큰 제안! 먼저 메모리 조각화를 막기 위해 하나의 larg 버퍼를 사용하기로 결정했습니다. 그러나 모든 바이트가 동시에 할당되는시기는 중요하지 않을 수 있습니다. – remdao

+0

단편화는 실제로 80K 이상인 버퍼의 경우에만 문제가됩니다. 단 80KB 이상의 할당이 압축을 얻지 않는 대형 오브젝트 힙에서 취해지기 때문입니다. GC가 콜렉션 후에 압축되어 조각화를 제거하는 정상적인 힙 (heap)은 더 적습니다. – AnthonyWJones

+0

나는 버퍼 관리자를 바이트 배열의 스택으로 구현하는 것이 더 낫다는 것을 깨달았다.왜냐하면 오프셋을 추적 할 필요가 없다면 루프를 가질 이유가 없기 때문입니다. 바이트 배열 객체를 밀고 팝하는 것이 더 빠를 것입니다. – remdao

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