테크 개발 메이플스토리의 64비트 클라이언트 도입 여정 2024.04.24. url 복사 facebook 공유 페이스북 공유 linkedin 공유 공유하기 <br> # 왜 64비트 클라이언트로 전환하게 됐을까 <span style="font-size: 16.5px">메이플스토리는 서비스가 처음 시작된 2003년부터 2021년 9월경 64비트 패치 전까지 오랜 기간 32비트 체제로 운영되어왔습니다. 서버들은 과거에 이미 모두 64비트로 전환이 되었지만, 다양한 환경의 많은 유저들이 직접 실행해야 하는 클라이언트는 호환성 이슈 등으로 인해 64비트로 전환하기가 쉽지 않았습니다. 하지만 게임 내 점차 쌓여가는 콘텐츠와 화려해지는 스킬 이펙트, 더욱 거대한 보스 몬스터의 등장으로 상당히 많은 메모리가 필요하게 되면서 32비트가 사용할 수 있는 메모리의 한계에 다다르게 되었습니다.<br><br>이론적으로 32비트에서는 최대 4GB의 메모리를 사용할 수 있지만 실제로 윈도우 시스템에서는 이보다 더 작은 2GB의 메모리만을 사용하게 됩니다. 간단히 설명 드리자면 응용프로그램과 시스템이 2GB씩 나눠서 메모리를 사용하기 때문인데요. 2GB는 게임을 돌리기에는 턱없이 부족한 용량입니다. 그래도 옵션을 통해 1GB를 더 확보할 수 있습니다. “Large Address Aware”라는 옵션인데 이를 통해 2GB+2GB로 나누던 것을 3GB+1GB로 조정해 응용프로그램이 3GB를 사용할 수 있습니다. 하지만 이정도로는 점점 거대해져가는 메이플컨텐츠를 담아내기엔 턱없이 부족한 용량이었습니다.<br><br>특히 주로 보스 사냥 때 메모리 부족 이슈가 빈번히 발생하는데요. 그렇기 때문에 그동안은 보스 콘텐츠 입장 시 메모리를 최대한 정리하거나 다른 유저의 스킬은 안보이게 하는 등 다양한 작업들을 거쳐 메모리 사용을 최대한 억제해왔습니다. 그럼에도 불구하고 결국 메모리가 부족해지는 상황이 발생하게 되면 캔버스 생성에 실패하게 되면서 이펙트나 아바타의 모습이 안보이기 시작합니다. 특히 우르스 같은 거대한 보스를 잡기 위해 고레벨의 캐릭터들이 스킬을 대거 사용하다 보면, 어느 순간 메모리 부족으로 인해 클라이언트가 종료되는 불상사가 일어나곤 했습니다.<br><br>이러한 문제점을 비롯해 이제는 게임 유저 대부분이 64비트 윈도우를 사용함에 따라 결국 64비트 클라이언트로의 전환을 결정하게 되었습니다.</span> <br><br> # 64비트 도입 과정에서 맞닥뜨린 문제와 해결 과정 <span style="font-size: 16.5px">메이플스토리에서는 개발 단계에서 64비트 빌드를 관리하지 않고 있었기에, 64비트 클라이언트로 전환하기 위해선 64비트 빌드 에러를 가장 먼저 해결해야 했습니다. 이 과정에서 기억에 남는 문제와 해결 방법을 몇 가지 소개 드리고자 합니다.</span> * <span style="font-size: 16.5px"> **`C1128` 에러** <br>디버그 빌드에서 가장 먼저 맞닥뜨린 에러였습니다. msdn 문서를 살펴보니 코드 컴파일 후 생성되는 각 obj 파일마다 최대 2^16(65,279) 만큼의 섹션 개수를 지정할 수 있도록 제한되어 있어, obj 파일에 기록 가능한 최대 섹션 수를 초과하면 C1128 빌드 에러가 발생하는 것입니다. <br><br>발생했던 원인은 복합적인데요. 디버그 빌드는 /Gy 옵션을 사용 중이었고, x64에는 pdata라는 섹션을 추가로 기록하기 때문에 몇몇 코드 양이 많은 cpp 파일에서 발생했습니다. 이에 `/bigobj` 빌드 옵션을 추가해 obj 파일에서 사용하는 섹션 수를 2^32(약 42억개) 만큼 늘려서 문제를 해결했습니다.</span> <br> * <span style="font-size: 16.5px"> **인라인 어셈블리 에러**<br>메이플스토리 클라이언트 코드에는 최적화 및 보안 이슈로 인해 인라인 어셈블리 코드가 존재하고 있었는데요. Visual C++ x64 컴파일러는 인라인 어셈블리를 지원하지 않아 빌드가 되지 않는 문제가 있었습니다. 다행히 인라인 어셈블리와 대응되는 컴파일러 내장 함수를 지원하고 있어, 컴파일러 내장 함수 교체로 문제를 해결했습니다. <br> * <span style="font-size: 16.5px"> **의존 라이브러리 체크**<br>메이플스토리 클라이언트에는 Dwarf(내장 브라우저), NGS 등 넥슨 사내 다른 팀에서 제공하는 모듈을 사용하고 있습니다. 이에 사용 중인 라이브러리의 x64 모듈을 링크 시켜주는 작업이 필요했습니다. 특별히 큰 문제는 없었지만, 버전이 오래된 모듈의 경우 x64를 지원하지 않아 버전업 과정을 거치는 등 손이 많이 가는 작업을 진행했습니다. <br> * <span style="font-size: 16.5px">**보안 코드 수정**<br>보안적인 문제로 인해 상세히 설명 드리긴 어렵지만, 클라이언트 해킹 방지를 위해 클라이언트 변조를 검출하는 기능이 있습니다. 이 과정에서 프로세스 내 특정 주소를 참조하게 되는데요. 64비트의 경우 PE-File 구조가 32비트와 달라 기본 주소 처리를 64비트에 맞게 처리를 해줘야 해서 해당 작업을 거쳤습니다. <br> * <span style="font-size: 16.5px">**메모리 맵 파일**<br>극적인 성능 향상을 위해 게임 데이터를 읽는 코드를 메모리 맵 파일 API로 교체했습니다. 체감되는 성능 향상이 있었지만 간헐적으로 메모리 사용량이 급증하는 문제가 있었는데요. 발생하는 원인은 (1)메모리 매핑 대상 파일인 wz 파일의 크기가 크고 (2)메모리 맵 파일로 읽을 경우 커널 메모리를 사용하기에 메모리 할당/해제 컨트롤이 되지 않는다는 두 가지 문제가 있었습니다.<br><br>당시 유저들에게도 체감되는 상황을 제공한 터라 메모리 맵 파일 사용을 포기할 수는 없어서 (1)아이템, 스킬 등 카테고리를 분류해 배포했던 wz 파일을 100MB 내외로 잘게 분할하여 배포하고 (2)이펙트, 필드, 스킬 같은 용량이 큰 데이터는 메모리 맵 파일 사용을 제외 처리하여서 메모리 급증 문제를 해결했습니다. <br><br> # 64비트 도입 이후의 렉 최적화 이야기 <span style="font-size: 16.5px">이처럼 많은 준비 과정을 거쳐 지난해 9월 메이플스토리에 64비트 클라이언트가 정식 적용됐습니다. 도입 직후 발생한 메모리 이슈를 해결하고 한시름 놓았다고 생각했지만 여러 유저들 사이에서 렉이 심하게 발생한다는 동향이 추가로 발생하는 것을 확인할 수 있었습니다.<br><br>게임 내 렉을 잡기 위해선 먼저 발생 환경을 재현해야 하는데요, 그동안 개발자 환경에서는 렉이 발생하지 않아 문제의 원인을 파악하기 까다로운 상황이었습니다. 메이플스토리 코드는 파일 하나만 해도 수만줄이 넘는 코드들의 집합으로 이루어져 있어 코드만 봐서는 원인을 찾기가 어렵기 때문에, 렉을 발생시키고 프로파일링(Profiling)을 통해 분석하여 원인을 파악해야 합니다. 마치 의사가 병의 원인을 파악하기 위해 MRI나 X레이를 촬영하는 것과 유사하다고 볼 수 있습니다.<br><br>그러던 중 몇몇 직원분들의 노트북 환경에서 렉이 발견된다는 것을 전달받아 문제 상황을 재현할 수 있었는데요. 주요 원인은 스마트포인터의 과도한 레퍼런스 카운팅(Reference Counting) 때문이었는데요. C++에서는 객체의 메모리 관리를 위해 스마트포인터를 거의 필수적으로 사용하게 됩니다. 덕분에 편리하게 메모리를 다룰 수 있지만 한가지 유의점이 있는데, 스마트포인터의 Reference Count가 Atomic연산자로 구현되어 있다는 점입니다. Atomic연산자는 가끔씩 호출하는 정도로는 시스템에 부하를 거의 주지 않지만 과도하게 호출하게 되면 시스템 전체에 꽤나 심한 렉을 유발시키곤 합니다.<br><br>보통 이런식의 코드인데요, 어떤 점이 문제인지 보이시나요? 바로 “auto aaa” 여기서 문제가 발생합니다. std::shared_ptr의 복사생성자가 호출되어 Reference Counting을 1증가시키고 또 다시 1감소시키는 작업을 반복적으로 하게 되어서 list의 size만큼 반복하게 되는 것입니다. <br> ```js std::list<std::shared_ptr>sampleList; for ( auto aaa : sampleList ) { … } ``` <br> <span style="font-size: 16.5px">이를 고치기 위한 방법은 아주 간단합니다. 이렇게 &만 붙여주면 복사생성자가 호출이 없어지기 때문에 아주 효율적인 코드로 바뀌게 됩니다. 이 케이스는 클라이언트뿐만 아니라 서버에서도 자주 발견되는 성능 이슈의 단골 손님이기도 합니다. ```js for ( auto& aaa : sampleList ) { … } ``` <span style="font-size: 16.5px">이처럼 문제점만 찾으면 보통 해결법은 어렵지 않은 편인 경우가 대부분이기에 작업 후 테스트를 거치며 렉이 해결되었는지 반복적으로 확인했습니다. 이후 수차례 패치 과정을 거쳐 현재는 유저분들이 겪던 렉 현상은 많이 개선된 상황입니다. 유저분들이 보내주시는 일부 렉 현상에 대해서는 계속해서 분석 및 작업을 진행 중이며, 앞으로도 쾌적한 메이플스토리를 위해 꾸준히 개선해나갈 예정입니다. <br><br><br> 13년차 라이브 서비스 프로그래머 #64비트클라이언트 #서비스프로그래머 #프로그래머 문의 url 복사 facebook 공유 페이스북 공유 linkedin 공유 공유하기
13년차 라이브 서비스 프로그래머