DB 정규화를 하지 않는 이유

DB 정규화를 하지 않는 이유는 한마디로 성능이다.

그렇다면 DB 정규화는 왜 성능을 해치는가?

1. DB 정규화는 테이블을 많이 나누게 하고, 테이블이 맣아지면 Join 이 많아진다. 그리고 Join 은 성능에 큰 악영향을 미친다.

1.1 Join 은 테이블 락 (Lock) 을 일으키고, 테이블 락은 병렬성의 큰 적이다.

아마 이 이유가 No-SQL 이 부상하게 된 가장 주된 원인이 아닐까 생각한다.

Python 의 GIL 이랑 같은 원리.

1.2 Join 은 여러 테이블을 가로지르는 다중 Disk-seeking 을 일으킨다. 한마디로 데이터 국지화의 적이다.

컴퓨터 하드웨어와 소프트웨어에서 많은 최적화장치들의 작동근거가 데이터 국지화이다. 이 가정이 깨질 경우 데이터 액세스가 굉장히 비싸진다.

DB 정규화는 공간을 가장 절약하는 방식이다 (i). 하지만 저장공간은 저렴하다 [1].

공간을 절약하는건 당연히 죄가 아니다. 심지어 공간을 절약해야 시간도 절약되는 경우가 많다. 그리고 이것 역시 “정규화가 성능에 좋다” 라고 주장하는 사람들의 한가지 근거이다.

하지만 알고리즘이란게 어느 선에 도달한 뒤에는 불가피하게 시간과 공간 사이의 트레이드오프에 직면하게 된다.

정규화는 공간 절약의 극한에 서있다.

이는 이론적으로도 당연하게 시간을 트레이딩 할 여지가 있음을 의미하지만, 많은 실천결과들이 역시 이 결론을 뒷받침해주고 있다.

또한 위의 1.2 와 결합시켜서 살펴보면 알게 되는데, 데이터 양을 줄인다고 무조건 시간이 절약되는게 아니라, 다수의 경우 데이터 국지화가 성능에 훨씬 큰 영향을 미친다. 이것은 실천적으로도 많이들 이중화 (Redundency) 로 정규화를 타파해 성능향상을 이룩하고 있는 산업현황과도 매칭된다.

또한 이 점은 저장 매개체의 랜덤 액세스와 순서적 액세스 사이의 수량급을 초월하는 성능 gap 의 배경속에서 더욱 확대된다. 이것은 물리적인 HDD 에서 유독 두드러지지만 SSD 에서도 상황은 비슷하다.

정규화는 쓰기에 최적화되어 있다. 하지만 읽기가 훨씬 중요하다.

Continue reading

슈퍼마리오 멀티플레이어 개발 후기

coolspeed

팀 합류 과제로 게임제작 미션을 받았다.

나는 과제로 “슈퍼마리오 멀티플레이어 게임”을 만들었다:

EnemyCD.gif

과제 결과물을 왓 스튜디오의 허락을 받고 소스코드를 Github 에 공개하였다:

https://github.com/coolspeed/MarioMultiplayer

관심 있으신 분들은 별표 하나씩 찍어 즐찾해주시길 부탁한다.

게임 룰: 위의 GIF 움짤  그대로. 먼저 상대방을 밟는 쪽이 이긴다.

과제가 1차, 2차로 나뉘는데 1차에서는 싱글 플레이어 슈퍼마리오를 만들었고 2차에서는 그것을 멀티플레이어 게임으로 발전시켰다.

최종 클라이언트 Java (Processing) 1500 줄, 서버 C# 22줄로 짜여졌다.

클라이언트

Processing 으로 만들었다. 왜?

    1. Processing 이 좋아서. (유쾌한 시각적 프로그래밍)
    1. Unity 쓸줄 몰라서.
    1. 다른 엔진도 쓸줄 몰라서.
    1. 엔진부분이 더 재밌어서.
  1. 웹에다 배포하고 싶어서. (P5.js 를 통하여. 하지만 망했다. 그냥 일반 Processing 애플리케이션처럼 exe 파일로 빌드했다.)

서버

단순한 릴레이 서버.

big_mirror.png

넷코드

ZeroMQ 를 네트워크 라이브러리로 사용했다.

Continue reading

왜 그동안의 함수형 언어 홍보는 잘못됐는가?

그렇다. 함수형 프로그래밍과 그것을 포함하는 “선언식 프로그래밍” [1] 은 그동안 이른바 산업 프로그래밍과 산업 프로그래밍 언어의 혁신요소들의 주요 원천 중 하나였다[2]. 하지만 그동안의 함수형 언어 홍보에서는 중요한 오류들을 범했다.

예를 들어 함수형 에반젤리스트들은 아래와 같은 광고어들을 내세웠었다.

“함수형 언어에서 변수는 값이 바뀔 수 없어요. 그리고 이게 좋아요. 버그를 막아주니까.”

이걸 듣는 사람들의 반응은 아마도 당장 발이 묶인 느낌일 것이다. 그래서 “그러면 코딩을 어떻게 해요?” 라고 묻는다면, 이런 대답이 올 것이다. “변수 변경이 정말 필요할 때는 변경할 수도 있어요. 그리고 함수형 프로그래밍에서는 꼬리 재귀를 사용하지요.” 그러면서 아마도 계승(factorial)이나 피보나치 예제를 주머니에서 꺼낼 것이다.

이렇게 되면 프로그래머는 아마도 고개를 갸우뚱 하며 (말은 하지 않고) 생각할 것이다.

“그렇게 되면 어차피 명령식에서랑 똑같이 변하는 변수를 사용하지 않을까? 과연 참고 변수를 변하지 않게 사용할 수 있을까?”

“그리고 나는 일할 때 매일 계승을 짜진 않는단 말야..흠..”

Continue reading

싱글스레드 모델이 액터 모델(Actor-Model)보다 나은 X가지 이유

1. 액션(동작) 순서 컨트롤이 된다. 액션의 페이스를 컨트롤할 수 있다. 예를 들어 30fps 로 모든 몹의 x좌표 더하기 1. 액터 모델에서는 비동기라서 그 액션이 어느 프레임에 들어갈찌 모른다.

2. 디버깅이 쉽다.
2.1 액터 모델에서는 글로벌 상태에 브레이크 포인트를 찍은 후에 매개인의 메일박스를 검사해야 하는 번거로움이..
서버의 경우 클라와 달리 브포보다 로그가 더 유용할 경우가 많다. 예를 들어서 Joe Armstrong 과 Richard Stallman 등 분들이 모두 printf 디버깅 방식을 더 선호한다는 소문이… 하지만 서버가 클라만큼 상태가 복잡할 경우, 브포(==시간정지) 를 통한 글로벌 상태 파악이 극히 중요해질 때가 있다.
2.2 그리고 액터 모델은 비결정론적이라서 어떤 버그들이 어떤때는 재현되고 어떤 때는 재현되지 않을 수 있다. (그러고보니 2.2 항목은 LMAX 의 장점만 말하면 다 나오네.)

3. 반경 10메터안 모든 몹들에게 범위공격. 액터모델의 경우 남의 좌표를 알 수 없어서 일일이 물어야 한다. “님 좌표 얼마임?” (그렇지 않으면 좌표를 좌표테이블에 등록해야 하는데, 그러면 액터모델이 아니게 됨) (IO 오버헤드, 코딩 코스트)

4. 로드밸런싱따위 필요없다. 액터모델의 경우 (스케쥴러에 따라 다르지만) (어차피 스케쥴러 연구하는 사람은 없으니) 액터 로드밸런싱이 필요한 경우가 있다(Skynet 역사 참조). 반면 싱글스레드 모델에서 프로그래머는 신이고, 로드밸런싱따위 필요없다.

5. 트랜잭션?

6. 클라도 싱글스레드 모델이잖아.

7. CPU 성능에 좋다 (동일언어) (사실 이건 가장 중요하지 않은 원인이다) (늘여쓰면 글한편감이라 자세한 내용은 생략한다).

결론: 두 모델이 서로 다른 문제 도메인 (Problem Domain) 에 적합할 것 같다는거다:

  • 액터모델은 격리성, 분산성과 faul-tolerance 가 골(goal)에서 제일 중요한 경우 또는 message-passing 자체가 문제를 잘 모델링할 경우 (IM, Instant Messenger: e.g. Facebook Messenger, Whatsapp, etc.)
  • 싱글스레드 모델은 글로벌 상태 컨트롤이 중요한 경우. 예를 들어 게임. (시뮬레이팅 문제?)

적재적소(適材適所).

길게 쓰면 아무도 안읽으니 이번에도 후딱 끝내는 미덕을 발양하겠다.

(끝)

[번역] ZeroMQ설계자: 세계를 구원하라

  • 번역: coolspeed

이 글은 ZeroMQ설계자가 지은 책이며 ZeroMQ의 “바이블”이라 불리는 ZGuide 의 머리말이다.

이 책은 다양한 프로그래밍 언어 예시 버전의  PDF, epub 등 이북 포맷으로 다운로드도 가능하고[1], 한글 번역도 온라인에서 읽을 수 있다. 당연히 그속에는 이 머리말의 한글번역도 포함되어 있었다. 다만 번역이 만족스럽지 못해 필자가 직접 번역했다.


ZeroMQ 100 자 요약

ZeroMQ (ØMQ, 0MQ, or zmq 라고도 표기함) 는 임베드블 네트워킹 라이브러리처럼 생겼는데 실제론 컨커런시 프레임웤처럼 작동한다. ZeroMQ 는 원자적으로 메시지를 배송하는 소켓을 제공하는데 그 배송수단으로는 스레드간 통신, 프로세스간 통신, TCP 그리고 multicast 등 다양하다. 당신은 ZeroMQ 소켓을 N-to-N 방식으로, fan-out, pub-sub, task distribution, 그리고 request-reply 등 패턴으로 사용할 수 있다. 이것은 클러스터 제품들의 뼈대로 사용하기에 전혀 문제 없는 높은 성능을 가지고 있다. 이것의 비동기 I/O 모델은 당신이 message-passing task처럼 짜여진, 확장성 있는 멀티코어 애플리케이션을 만들 수 있게 해준다. 언어 API 만큼 고득점을 갖고 있으면서 거의 모든 OS에서 돌아간다. ZeroMQ 는 iMatix 에서 개발했으며 LGPLv3 라이센스로 소스가 오픈되어있다.

 

세상을 구원하다

ZeroMQ 요놈은 정말 좋은데. 어떻게 말로 표현할 방법이 없네.  ZeroMQ의 가치를 설명하기 위해, 일부 사람들은 요놈이 제공하는 모든 훌륭한 기능들을 나열하는 것으로 부터 시작하려고 한다:

  • 이놈은 스테로이드를 처먹은 소켓이야.
  • 이놈은 라우팅 기능이 있는 우편함으로 비유할 수 있지.
  • 존나 빠름.

Continue reading

Skynet 의 UDP 지원

작자: cloudwu

번역: coolspeed

오랜 고민 끝에 결국 skynet (https://github.com/cloudwu/skynet) 에 기본적인 UDP 지원기능을 넣기호 했다. 대부분 경우, 특히 네트워크 게임 영역에 있어서 나는 UDP 프로토콜 사용을 반대하지만 skynet 은 이미 게임영역에만 사용되는게 아니라는 점을 고려하여 UDP 지원을 추가하는게 의미있는 일이라고 생각했다.

그나저나 최근의 피드백상으로 보면 어떤 사람은 skynet 을 이더넷 스위치 (Ethernet Switch) 구현에 사용했고 (사용한 CPU 가 powerpc 의것이여서 Big / Small Endian 관련된 버그들을 해결하는데 도움이 됐다고 한다), 증권분야에 사용한다는 사람도 있었다. 그리고 비디오 방송 분야에서 사용한다는 사람도 있었다. 그리고 아마 skynet 을 web 개발에 사용하는 사람도 있을 것이다. 간단한 테스트 결과에 의하면 성능면에서 lua 를 nginx 에 접합시켜 사용하는 것보다 못지 않았다.

현재 UDP 부분의 코드는 이미 완성되였는데 github 에서 “udp”라는 브랜치에 넣었다. 머지 않아 마스터 브랜치에 머지할 생각이다. 나는 이쪽의 니즈가 없어서 이런 니즈가 있는 친구들이 코드를 읽고 실제로 사용해주기를 바란다. 그래야 잠재 보그들이 발견될거니까.

이 부분의 설계와 일부 api 문서들은 wiki 에 보충해넣었다. ( https://github.com/cloudwu/skynet/wiki/Socket ) (중문)

Written in November 13, 2014
Translated in December 24, 2015

원문링크: http://blog.codingnow.com/2014/11/skynet_ae_udp_oeoe.html

Skynet 설계 설명

작자: cloudwu

번역: coolspeed

한달이란 시간에 걸쳐 드디어 skynet (https://github.com/cloudwu/skynet) 의 C 버전을 완성했다. 중간에 여러 모듈을 반복하여 리팩토링 한 결과 최종 남은 코드는 얼마 되지 않았다: 겨우 C 코드 6천여줄, 그리고 Lua 코드 천여줄이었다. 비록 일부 코드는 좀 급하게 짰지만 나 자신의 퀄리티 요구에 부합된다. 버그는 피면하기 어렵겠지만 이정도 작은 규모의 코드베이스는 충분히 명료하여 수정하기가 어렵지 않을 것이다.

Github 에 올린 이 프로젝트를 개발하는데 사용된 시간은 사실 한달도 훨씬 안된다. 나의 대부분 시간은 지난 반년 넘게 짜놓은 Erlang 버전 프레임워크와의 하위호환성과 호환 안되는 Erlang 모듈들을 포팅하는데 허비했다. 이 부분 코드들은 저희의 실 게임프로젝트와 관련 있는 것이어서 오픈하지 않았다. 또한 양이 이보다 몇배나 되는 관련코드들을 왕창 올려봤자 이 오픈소스프로젝트의 의미에 도움이 되지 않을 것이다. 프로젝트에 관심을 가지는 친구들은 그런 별로 중요하지도 않고, 많은 인터페이스들이 레거시 부담으로 추하게 설계된 구조 때문에 오히려 미궁에 빠질 것이다.

저희의 구 프로젝트와 연동시켜 보고 포팅도 다 잘 됐음이 확인된 후에 나는 또 skynet 의 일부 밑층 설계를 수정했다. 안전한 포팅을 보장하는 전제하에서 최대한 개선을 함으로써 히스토리컬 레거시를 최소한으로 걺어지도록 힘썼다. 이런 수정들은 쉽지 않았지만 의미가 있다고 생각한다. 최근 내가 자세하게 생각을 굴린 성과들이다. 오늘의 이 블로그글에서 나는 이번 픽스된 버전을 토대로 설계 설명을 좀 기록하려고 한다. 추후 찾아보기에도 좋게.


Skynet 코어는 어떤 문제를 해결하는가

나는 우리의 게임 서버가 ( 하지만 skynet 이 게임서버에만 사용될 수 있는게 아니다) 충분히 멀티코어의 파워를 이용할 수 있기를 바라며 서로 다른 비즈니스 로직들을 서로 독립적인 실행환경속에서 협동적으로 작동시키기를 원한다. 최초에 나는 이런 실행 환경이 OS 의 프로세스이기를 바랬다. 하지만 후에 발견한건데 만약 우리가 임베디드 언어를 필연적으로 사용하게 된다면, 예를 들어서 Lua, 독립적인 OS 프로세스의 의미가 크지 않을 것이다. Lua State 는 이미 충분히 양호한 샌드박스 환경을 마련해주어 서로 다른 실행환경을 격리해줄 수 있었다. 뿐만아니라 멀티스레딩 방식은 상태 공유를 통해 데이터 교환을 더욱 효율적으로 할 수 있게 해준다. 그러면서 멀티스레딩이 흔히 비평받는 단점들, 이를테면 복잡한 스레드 락, 스레드 스케쥴링 등은 밑단의 규모를 가능껏 제한하고 설계를 최대한 간소화함으로써 최소한의 범위안으로 가두어둘 수 있다. 이런 입장에서 skynet 은 최종적으로 3000 줄 안되는 C 코드로 코어층을 구현했는데 이것은 코딩 좀만 할줄 아는 C 프로그래머라면 빠른 기간내에 이해하고 유지보수 할 수 있는 규모이다.

핵심기능차원에서 skynet 은 오직 한가지 문제만 해결한다:

하나의 규약에 부합되는 C 모듈을 다이나믹 라이브러리 ( so 파일) 로부터 가동시켜 영원히 겹치지 않는 (모듈이 꺼지더라도) 하나의 숫자 id 에 바인딩하여 handle 로 사용하는 것이다. 모듈은 서비스 (Service) 라 불리고 서비스들은 서로 자유롭게 메세지를 보낼 수 있다. 모듈은 skynet 프레임워크에 하나의 callback함수를 등록하여 그것으로 자기에게 보내온 메세지를 받는다. 모듈들은 모두 하나하나의 메세지에 의해 드리븐 되고 메세지가 오지 않을 때에는 펜딩되여있으며 CPU 자원 소모가 0이도록 보장된다. 자발 실행적인 로직이 필요하다면 skynet 시스템에서 제공하는 timeout 메세지를 이용하여 일정 시간마다 트리그 시킬 수 있다.

Continue reading