반면교사

요즘 모종의 코드 — 부모가 버려버린(?) — 을 보고있는데 참 보고 배울게 많다(?).

서버 로직에서 네트웍과 로직 부분을 분리하라

이 둘이 같이 붙어있으면 사실 상 리팩터링하는 수 밖에 없다. 둘을 따로 테스트 할 수도 없고, 그냥 지뢰밭이 된다(..). 고치는건 살얼음길…

프로토콜 메시지의 형태(C++의 struct/class)가 그대로 서버 로직과 같이 움직이면 이건 어쩌자는거냐(…). 즉, 프로토콜 메시지 형태를 바꾸는 일 == 전체 서버의 재컴파일 및 로직 부분을 리뷰해야하는 사태가 생기는 코드를 보고 있자니 느는건 한숨이오 Orz

게다가 패킷을 생성하고(…) 보내는 코드가 한 곳(class or function)에 응집되어있지 않고, 수많은 곳에서 조용히 패킷을 만들고 보내는 일을 하고 있다. 싸우자!

그래도 제일 무서운 건 이것. 대체 왜 프로토콜 메시지를 위한 구조체들을 DB 쪽 래퍼에서 알고 있고, 이걸 일일이 바인드(SQL Bind)해서 쓰고있는데? -_-

 

불필요한 메모리 할당은 왜 하는가

이 코드베이스에선 대부분의 네트웍 전송을 위한 준비 단계에서,

  • 동적으로 객체를 위한 메모리를 할당받고
  • 객체에 다른 (보낼) 객체를 저장하고
  • 이를 다시 실제로 보낼 컨테이너 객체 류에 삽입

한다는 것, 보내는 곳에서도 이 일을 하는데 더 이상한건(…) 이걸 결국엔 다 지워 -_-;; 버린다.

소멸자에서 버그가 있는 경우도 있고 Orz

C++ 의 강점 중의 하나는 메모리 할당 공간을 프로그래머가 제어할 수 있다는 점일 터인데; 저런 용도라면 아주 자연스러운 공간이 있질 않은가 -_-;; 스택이라고(…).

 

불필요한 캐슁

가장 어이없었던 부분이기도 한데 대부분의 객체 관리자가,

  • 객체
  • 객체의 네트웍 전송 형태(Packet 을 보내기 위한 버퍼 형태로)

두 가지를 다 가지고 있다. 대체 왜? -_-; 게다가 내부 업데이트에 객체가 아니라 객체의 네트웍 전송형태를 주고받기도 하고 -_-;(덕분에 애써 de-serialize한 객체를 다시 serialize -> deserialize 해가면서 쓴다)

개인적으론 메모리 소모량이 적은 서버 프로그램이 전반적인 반응성도 좋다고 본다. 메인 메모리가 빨라봐야 L1/L2 캐쉬에 비하면야 -_-;;

일단 캐슁하기 위해 불필요한 연산이 들어가고 / 이게 바뀔 수도 있으니 (특히 분산 서버에서) consistency도 맞춰줘야하고 / 그리고 로컬 객체를 패킷으로 만들어 보내기 위한 연산은 대부분 CPU-local 한 연산이라 CPU를 늘릴 수 있는 가능성만 있으면 우선순위가 아주 낮은 최적화 작업이다.

Warning은 무슨 맨허

서버 쪽 코드만 주로 보는데, 이쪽을 풀 빌드하면 현재 3948개의 compiler-warning을 보게된다. 요 몇 일 사이에 이걸 최대한 줄이려고 삽질 중이긴한데, 이걸 줄이다보니 실제로 의미있는 경고 메시지가 수 개 발견된다 -_-;;

warning이 좀 뜨면 그걸 해결해놔야지 대체 개발을 어찌하려고 이 모양이었는지.

사실 이 원인 중에 하나는 매크로를 지나칠 정도로 사용했다는게 문제인 것 같지만; inline 함수로 처리하거나 멤버 함수 하나 추가하면 될 일을 / 상속 계통의 최상 위에 함수를 만들면 될 일을, 타입과, 객체를 일일이 지정하는 매크로로 해놓으니 코드 보기가 뭣 같더라.

 

정말 이러지 말아야지 싶은 코드를 한가득 보고 있다. “개발 과정”에 관한 것만으론 필생의 적을 만난 것 같은 기분?

Author: rein

나는 ...

13 thoughts on “반면교사”

  1. 제가 물려받은 “해외” 코드 중에는, 무려 1-thread per 1-user 에다가 메시지 핸들러에 blocking db query 가 순차 처리로 이루어진 것도 있습니다. 헤헤.

    1. Reiot / 예전에 GPG에서 유저당 스레드 5개 띄운 서버를 짰는데로 시작하는 스레드를 본 기억도 있습니다(…). 흑흑

  2. C++로 클래스를 써서 짜기는 했는데 ..
    상속 없음, 로직과 I/O는 한곳에 있고, 함수 하나에 최대 1500 라인, 변수의 상당수가 Global or Static ..
    Thread sync는 전부 Event로 처리했는데, 디자인을 잘못해서 Running Thread는 한개 […]
    구조체 하나는 380MB(!)의 용량을 가지고 있고 ..
    (이게 왜 변수로 선언이 안되냐고 질문을 하기도 […])
    Warnnig 300여개중에 미 초기화 관련이 반이 넘고 …

    저런 돌아가는 것 자체가 미라클인 코드가 제 첫직장에서 받은 코드였습니다 […]
    이후로 저런 극도로 아름다운[?] 코드는 본적이 없는것 같네요.

    보다보면 저질코드[..]들은 공통점들이 꽤 있는게, 배우는 방법이 비슷하지 않았나 싶기도 하더군요.

  3. DarkAngel / 처음에 읽다가 “소스 파일이 1.5k라인인게 무슨 문제지” 라고 잠시 고민했습니다(…).

    근데 돌아가는게 어디에요 버그만 없다면야(…);

    나중에 저런 코드를 보게되는 사람들이 배우는게 있다면 그 이후에는 조금씩 나은 세상(…)이 올꺼에요!(…)

  4. MCV가 제대로 분리되어 있지 않고 불필요한 메모리 할당이 잔뜩 되어 있는 코드네요. cpp 별로 나눠서 코딩을 하면 저런 사태가 자주 벌어지더라구요. 누군가가 MCV를 잡아서 프로토타입을 만들고 거기에 기능을 ADD 하는 식으로 작업을 하면 저런 사태를 방지할 수도 있지 않을까 하는데요. 뭐 그래봐야 저도 유지보수맨이라서 해본 적은 없지만요.

    1. 게임 서버에서 MCV 구조를 쓰는건 이상하긴한거 같지만.
      뭐 여튼 난 CPP 별로 나눠서 코딩하는데(…) 흑흑.

      ps. Warning은 현재 2000개 밑으로 내렸단다 :P

Leave a Reply