KGC2012: Avoiding Developer Taxes
GDC 2012의 “Robustification Through Introspection and Analysis Tools (Avoiding Developer Taxes” 와 유사한 내용인 듯.
강연 내용에 해당하는 코드(clang-extract)는 https://github.com/sk-havok/clang-extract 에서 볼 수 있다.
내용 요약:
고질적인 게임 개발 비용들(taxes)
직렬화
메모리 정보 보기
스크립트 바인딩
버전 관리
Reflection
C++로 개발할 때 reflection 을 이용해서 푼다. 여기엔 이런 문제가 있다:
- 어떻게 리플렉션을 만들까?
- 어떻게 (소스 코드와 데이터에) 싱크가 맞을까?
- 견고성 (robustness)
수동으로 처리하는 것은 문제가 많다. (buggy!)
regex 등으로 헤더를 처리하는 것도 마찬가지.
그리고…
- 하지만 C++에는 reflection이 없다
- GCC XML을 써봤다. 느리고 개발도 완성 상태가 아님 1
- clang + LLVM frontend를 수정해서 reflection 생성기 개발
C++ headers –> LLVM + clang –> DB 로 변환 그리고 이걸 이용해서 리플렉션 생성, 스크립트 생성. 그리고 정적 분석.
그리고 이 리플렉션을 쓰면 직렬화는 껌(…).
메모리 alignment를 해석해서 구조체의 메모리 배치를 효율적으로 바꾼다거나 하는 것도 가능.
LLVM+clang pass는 견고하고, 싱크가 안 맞는 문제를 방지
DB Consumer pass 는 컴파일 이전에 논리적인 문제를 찾고, 런타임 문제를 컴파일 시간에 잡게 한다.
유닛 테스트 역시 (자동으로) 리플렉션 데이터를 검사할 수 있게 됨.
Language Binding
C++을 스크립트에 바인딩 하려면 문제가 많음. 다만 리플렉션을 쓰면 데이터는 자연스럽게 됨. 그렇지만 callable들은 여전히 힘듬.
함수 trampoline 의 경우 C++ 함수 signature를 처리하기 위해서 생성하는데, 함수 signature마다 bridge 함수를 만드는 쪽이 함수마다 wrapper를 만드는 것보다 싸다.2
메모리 리포팅
Leap frog 테크닉을 쓴다: (일부) 메모리 할당의 타입을 기록하고, 이 오프셋과 사이에 있는 데이터를 유추. (indirect pointers)
그리고 stack walk하면서 이에 대한 데이터를 추정해서 메모리 할당 맵을 offline으로 생성할 수 있게 된다. 그렇지만 일부 컨테이너 류 등에서는 false positive가 있을 수 있고, obfuscated pointer 3 나 타입을 알 수 없는 void* 류는 처리 안됨. 그래도 디버그 모드는 콜스택과 시간을 남겨서 처리한다.
…로 annotated memory data를 만들 수 있게 함.
버전 관리
버전 관리는 쉽지 않음. 매뉴얼하게 하면 버그 나오기도 매우 쉽고 코드 관리도 힘들다.
이걸 snapshot versioning으로 구현. (reflection data의 CRC 이용해서 reflection 결과가 바뀌는 걸 추적)
하지만 이것의 오버헤드 때문에 좀 더 세밀한 단위로 변경 사항을 추적하고 이걸 patching하는 코드를 (semi-automatic하게) 만들게 함.
결론
코드 말고 데이터를 생성하자. 데이터가 더 작고 용도도 많다.