더북(TheBook)

4.4.1 희소 배열(sparse array) 압축

데이터 압축의 왕도는 바로 네트워크상 보낼 필요가 없는 정보를 제거하는 것에 있다. 이렇게 쓸데없는 정보는 드문드문하거나 완전히 채워져 있지 않은 데이터 구조에서 흔히 보게 된다. RoboCatmName 필드를 보자. 어떤 이유에서인지 RoboCat의 원작자는 RoboCat의 이름을 담아두는 최선의 방법으로 데이터 중간에 128바이트 문자 배열을 확보해 두는 게 좋다고 믿은 모양이다. 스트림의 멤버 함수 WriteBytes(const void* inData, uint32_t inByteCount)로 문자열 배열을 통째로 임베딩하면 되긴 하지만 보다 사려 깊게 구현하면 128바이트 전체를 기록하지 않고도 필요한 데이터만 직렬화할 수 있다.

여러 압축 알고리즘에서 쓰는 전략은 일반적인 경우를 상정해 보고 여기서 출발해 최적화할 때 취할 수 있는 이점을 이용하자는 것으로, 우리도 비슷한 전략으로 접근하고자 한다. 영어 문화권에서 많이 쓰이는 이름들, 그리고 로보캣의 게임 기획을 살펴보았더니 플레이어가 자신의 RoboCat 이름을 지을 때 128 글자를 모두 쓰지 않을 가능성이 매우 높다는 사실을 알게 되었다. 배열 길이가 얼마가 되건 마찬가지다. 최악의 경우를 상정하여 공간을 확보했다고 해서 직렬화 코드를 작성할 때 모든 사용자의 이름이 전부 최악의 경우에 해당된다고 가정할 필요는 없는 것이다. mName 필드를 살펴보고 실제 몇 글자를 이름으로 쓰고 있는지 세어보는 커스텀 직렬화 코드가 있다면 공간을 절약할 수 있다. mName이 널 종료 문자열이라면 그냥 std::strlen() 함수를 쓰면 된다. 이렇게 이름을 보다 효율적으로 직렬화하게 고친 예를 들면 다음과 같다.

void RoboCat::Write(OutputMemoryStream& inStream) const
{
     // 위에서 다른 필드를 직렬화함
    uint8_t nameLength = static_cast<uint8_t>(strlen(mName));
    inStream.Write(nameLength);
    inStream.Write(mName, nameLength);
    
}

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.