[C#] ArraySegment란?

Arthur·2023년 11월 6일
1
post-thumbnail

핵심 키워드 및 내용 요약


ChatGPT 요약

게임 서버 개발에서 데이터 전송을 위한 버퍼(Buffer)의 개념과 이를 C#에서 구현하기 위한 ArraySegment를 소개하고 있습니다. 버퍼는 데이터를 일시적으로 저장하고 전송할 때 사용되는 메모리 영역이며, C#에서는 ArraySegment를 활용하여 구현할 수 있습니다. ArraySegment는 배열의 특정 부분을 참조하는 용도로 사용되며, 메모리를 효율적으로 활용하여 재사용할 수 있습니다. 이를 게임 서버에서 활용한 예시 코드도 제시하며, Array와 ArraySegment의 차이, 활용 방법 등을 설명하고 있습니다.



게임 서버에서의 버퍼


게임 개발 프로젝트를 하면서 서버 파트를 맡게 되었습니다.
여기서 클라이언트와 서버가 데이터를 주고받기 위해서 버퍼라는 개념을 사용했습니다.

버퍼는 데이터를 한 곳에서 다른 한 곳으로 전송할 때 일시적으로 데이터를 보관하는 메모리의 영역을 뜻합니다.

이런 버퍼 영역은 일정한 메모리 영역을 전송이 완료된 데이터를 다시 비워주고,
보낼 데이터를 다시 채워주는 역할을 해야 합니다.

C#에서는 이런 버퍼를 Array가 아닌 ArraySegment를 사용해서 구현할 수 있습니다.



ArraySegment란?


ArraySegment는 해당 배열의 요소 범위를 구분하는 배열 주위의 래퍼입니다.
여러 ArraySegment 인스턴스가 동일한 원래 배열을 참조할 수 있으며 겹칠 수 있습니다.
원래 배열은 1차원이어야 하며 0부터 시작하는 인덱싱이 있어야 합니다.

  • 배열의 일부만 나타내는 개체를 상대적으로 비용이 많이 드는 메서드 Copy를 호출하는 대신 메서드에 인수로 전달 ArraySegment 하여 배열의 일부 복사본을 전달할 수 있습니다.
  • 다중 스레드 앱에서 구조를 사용하여 각 스레드가 ArraySegment 배열의 일부에서만 작동하도록 할 수 있습니다.

세그먼트(Segment)란?

세그먼트는 여러 산업 분야(컴퓨터, 마케팅 등)에 사용되는 단어입니다.
간단하게 보면 분할, 구간, 세부 부분을 쪼개는 것을 뜻합니다.

컴퓨터 분야에서는 네트워크 통신에 대한 것과 메모리에 세그먼트를 사용합니다.
메모리에서는 물리적인 단위가 아닌 논리적 내용 단위로 자르는 것 방법을 세그먼테이션이라고 합니다.

여기서 논리적인 단위는 모두 같은 단위로 나누는 것을 뜻하고,
세그먼테이션은 각각 다른 크기로 나누는 것을 뜻합니다.


위 내용을 정리하면 아래와 같습니다.

  • ArraySegment는 배열(Array)의 특정 데이터를 참조할 수 있게 해주는 레퍼(Wrapper)이다.
  • ArraySegment를 사용하여 1차원 배열의 래퍼로 배열 내의 요소를 길이(범위)를 지정하여 구분합니다.
  • 구분되어 있는 배열의 범위를 참조하기 때문에 사용할 때 새로운 배열을 만들지 않습니다.
  • ArraySegment는 불변(immutable) 구조체(struct)이기 때문에 내용을 변경할 수 없습니다.
    • 원본 배열을 보호히면서 배열의 일부를 참조하고자 할 때 유용합니다.


Array와 ArraySegment의 차이


배열의 특정 위치에서 특정 크기만큼 참고하고 싶을 때
보통 새로 배열을 만든 후 복사해야 원하는 데이터만 참조할 수 있습니다.

하지만 ArraySegment를 사용하면 새로 배열을 만들지 않으면서
버퍼의 특정 데이터를 참조할 수 있습니다.

// 원본 배열 생성
int[] originalArray = { 1, 2, 3, 4, 5 };

// ArraySegment 생성
ArraySegment<int> segment = new ArraySegment<int>(originalArray, 1, 3);

// ArraySegment를 사용하여 요소에 액세스
int element = segment[0]; // element에는 2가 저장됨


게임 서버에서의 ArraySegment의 활용


public class SendBuffer
{
    byte[] _buffer;
    int _usedSize = 0;

    public int FreeSize { get { return _buffer.Length - _usedSize; } }

    public SendBuffer(int chunkSize)
    {
        _buffer = new byte[chunkSize];
    }

    public ArraySegment<byte> Open(int reserveSize)
    {
        if (reserveSize > FreeSize)
            return null;

        return new ArraySegment<byte>(_buffer, _usedSize, reserveSize);
    }

    public ArraySegment<byte> Close(int usedSize)
    {
        ArraySegment<byte> segment = new ArraySegment<byte>(_buffer, _usedSize, usedSize);
        _usedSize += usedSize;
        return segment;
    }
}

게임 서버에서 클라이언트로 패킷을 전송할 때 Buffer를 활용하기 위해 구현된 코드입니다.
Buffer를 사용할 때 Array를 여러번 생성하는 것이 아닌, ArraySegment를 사용해서 참조하면서 배열을 재활용 할 수 있습니다.

  • Array의 사이즈를 최초에 chunkSize로 정의합니다.
  • 해당 Buffer Array의 각 구간에 전송할 데이터를 저장하고 사용합니다.
  • 전송할 때 데이터가 있는 영역만 담아서 전송합니다.


작성하면서 느낀 점


게임 서버 Rookiss님 강의를 들으면서 Buffer의 개념을 이해하는데 꽤 어려웠습니다.
갑자기 ArraySegment라는 새로운 개념과 배열을 효율적으로 사용하기 위한 코드가 이해하기 힘들었습니다.

아무래도 강의를 보고 구현을 할 당시에 ArraySegment 라는 키워드를 주의깊게 찾아보고 정리하지 않았던 것이 큰 것 같습니다.

이번에 정리를 하면서 Array와 ArraySegment에 대해 좀 더 알게 되었습니다.
ArraySegment를 Array를 대신하기 위한 것이 아닌,
Array를 참조를 통해 필요한 구간을 참조해 재사용 할 수 있게 도움을 주는 래퍼입니다.



참고 자료


  • MS docs - ArraySegment 구조체 => 링크
  • C# - ArraySegment 배열을 새로 할당하지 않고 특정 위치의 내용을 참고 하고 싶을 때 => 링크
  • 인프런 - ArraySegment 질문입니다 => 링크
  • 위키백과 - 버퍼 (컴퓨터 과학) => 링크
  • 운영체제 20장 - 메모리 관리(7) : 세그멘테이션(Segmentation) => 링크
profile
기술에 대한 고민과 배운 것을 회고하는 게임 서버 개발자의 블로그입니다.

1개의 댓글

comment-user-thumbnail
2023년 11월 7일

잘 배워갑니다~!

답글 달기