넥슨코리아 사내 발표자료로 왓 스튜디오에서 파이썬으로 《야생의 땅: 듀랑고》 서버를 비롯한 여러가지 도구를 만든 경험을 공유합니다.
- 게임서버와 각종 툴, 테스트/빌드/배포 시스템을 만들 때 사용한 재료
- 파이썬 코드 품질 개선, 디버깅, 프로파일링, 최적화
- 파이썬 오픈소스 생태계와 왓 스튜디오가 하는 오픈소스 활동
도커 무작정 따라하기: 도커가 처음인 사람도 60분이면 웹 서버를 올릴 수 있습니다!pyrasis
도커 무작정 따라하기
- 도커가 처음인 사람도 60분이면 웹 서버를 올릴 수 있습니다!
도커의 기본 개념부터 설치와 사용 방법까지 설명합니다.
더 자세한 내용은 가장 빨리 만나는 도커(Docker)를 참조해주세요~
http://www.pyrasis.com/private/2014/11/30/publish-docker-for-the-really-impatient-book
어느 해커쏜에 참여한 백엔드 개발자들을 위한 교육자료
쉽게 만든다고 했는데도, 많이 어려웠나봅니다.
제 욕심이 과했던 것 같아요. 담번엔 좀 더 쉽게 !
- 독자 : 백엔드 개발자를 희망하는 사람 (취준생, 이직 희망자), 5년차 이하
- 주요 내용 : 백엔드 개발을 할 때 일어나는 일들(개발팀의 일)
- 비상업적 목적으로 인용은 가능합니다. (출처 명기 필수)
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...Amazon Web Services Korea
서비스 런칭을 위해 라이온하트와 카카오게임즈가 어떻게 최적 성능의 인스턴스를 선택하고, Windows 운영 체제를 최적화하며, 왜 Amazon Aurora를 기본 데이터베이스로 채택하였는지를 설명합니다. 또한, 출시부터 운영까지의 과정에서 MMORPG가 어떻게 AWS 상에서 설계되고, 게임 서버 성능을 극대할 수 있었는지에 대해 전달해드립니다.
"손코딩뇌컴파일눈디버깅" 모임을 소개합니다.
백문이 불여일런, 트라이얼앤에러(Trial and Error) 식의 몹쓸 교육을 받아 온 개발자들이 코딩하기 전에 신중하고 꼼꼼하게 생각해보기란 쉽지 않습니다.
개발 시간 중 디버깅 시간이 절반 이상을 차지하고 있는 실정에 버그를 줄이기 위해 TDD니 유닛테스트니 많은 방법들이 개발되고 있지만 가장 일차적으로 중요한 것은 개발자들이 꼼꼼히 따져보는 것이 아니겠는지요?
미국의 선진 SW회사들은 이미 화이트보드에 PS문제를 푸는 것을 인터뷰 방식으로 채택하고 있습니다. 이는 이와 같은 풀이 방식이 개발자들의 기본 역량을 측정하기에 알맞은 지표라는 것이고, 개발자들이 기본적으로 갖춰야 할 역량이기도 하다는 것 입니다.
또한 자신의 생각을 명확하게 정리하고 다른 사람이 이해할 수 있도록 전달하는 Communication Skill 도 개발자가 갖춰야 할 역량 중 하나 입니다. 알고리즘을 어떻게 구현할 것인가를 팀원들과 소통하면서 자연스럽게 생각을 정리하고 전달하는 연습도 할 수 있습니다.
컴퓨터에 앉아 코딩하기 전 펜과 종이를 들고 눈과 머리와 손을 굴려 보시는 것은 어떠신지요??
넥슨코리아 사내 발표자료로 왓 스튜디오에서 파이썬으로 《야생의 땅: 듀랑고》 서버를 비롯한 여러가지 도구를 만든 경험을 공유합니다.
- 게임서버와 각종 툴, 테스트/빌드/배포 시스템을 만들 때 사용한 재료
- 파이썬 코드 품질 개선, 디버깅, 프로파일링, 최적화
- 파이썬 오픈소스 생태계와 왓 스튜디오가 하는 오픈소스 활동
도커 무작정 따라하기: 도커가 처음인 사람도 60분이면 웹 서버를 올릴 수 있습니다!pyrasis
도커 무작정 따라하기
- 도커가 처음인 사람도 60분이면 웹 서버를 올릴 수 있습니다!
도커의 기본 개념부터 설치와 사용 방법까지 설명합니다.
더 자세한 내용은 가장 빨리 만나는 도커(Docker)를 참조해주세요~
http://www.pyrasis.com/private/2014/11/30/publish-docker-for-the-really-impatient-book
어느 해커쏜에 참여한 백엔드 개발자들을 위한 교육자료
쉽게 만든다고 했는데도, 많이 어려웠나봅니다.
제 욕심이 과했던 것 같아요. 담번엔 좀 더 쉽게 !
- 독자 : 백엔드 개발자를 희망하는 사람 (취준생, 이직 희망자), 5년차 이하
- 주요 내용 : 백엔드 개발을 할 때 일어나는 일들(개발팀의 일)
- 비상업적 목적으로 인용은 가능합니다. (출처 명기 필수)
오딘: 발할라 라이징 MMORPG의 성능 최적화 사례 공유 [카카오게임즈 - 레벨 300] - 발표자: 김문권, 팀장, 라이온하트 스튜디오...Amazon Web Services Korea
서비스 런칭을 위해 라이온하트와 카카오게임즈가 어떻게 최적 성능의 인스턴스를 선택하고, Windows 운영 체제를 최적화하며, 왜 Amazon Aurora를 기본 데이터베이스로 채택하였는지를 설명합니다. 또한, 출시부터 운영까지의 과정에서 MMORPG가 어떻게 AWS 상에서 설계되고, 게임 서버 성능을 극대할 수 있었는지에 대해 전달해드립니다.
"손코딩뇌컴파일눈디버깅" 모임을 소개합니다.
백문이 불여일런, 트라이얼앤에러(Trial and Error) 식의 몹쓸 교육을 받아 온 개발자들이 코딩하기 전에 신중하고 꼼꼼하게 생각해보기란 쉽지 않습니다.
개발 시간 중 디버깅 시간이 절반 이상을 차지하고 있는 실정에 버그를 줄이기 위해 TDD니 유닛테스트니 많은 방법들이 개발되고 있지만 가장 일차적으로 중요한 것은 개발자들이 꼼꼼히 따져보는 것이 아니겠는지요?
미국의 선진 SW회사들은 이미 화이트보드에 PS문제를 푸는 것을 인터뷰 방식으로 채택하고 있습니다. 이는 이와 같은 풀이 방식이 개발자들의 기본 역량을 측정하기에 알맞은 지표라는 것이고, 개발자들이 기본적으로 갖춰야 할 역량이기도 하다는 것 입니다.
또한 자신의 생각을 명확하게 정리하고 다른 사람이 이해할 수 있도록 전달하는 Communication Skill 도 개발자가 갖춰야 할 역량 중 하나 입니다. 알고리즘을 어떻게 구현할 것인가를 팀원들과 소통하면서 자연스럽게 생각을 정리하고 전달하는 연습도 할 수 있습니다.
컴퓨터에 앉아 코딩하기 전 펜과 종이를 들고 눈과 머리와 손을 굴려 보시는 것은 어떠신지요??
.NET을 처음 접한 프로그래머가 P2P 네트워킹 기능을 구현하면서 마주쳤던 문제와 해결 방법등 개발 경험 전반에 걸쳐서 이야기 해 보려 합니다. 또한 C# 8.0에 추가되는 비동기 스트림을 미리 써볼 수 있는 AsyncEnumerable과 비동기 잠금(lock) 등의 편리한 기능을 갖춘 AsyncEx등의 라이브러리들도 소개합니다.
6. def calc():
return max([random() for x in
xrange(20000000)])
!
if __name__ == '__main__':
print(calc())
print(calc()) 4.418s
7. if __name__ == '__main__':
threads = []
for i in xrange(2):
threads.append(threading.Thread(target=calc))
threads[-1].start()
for t in threads:
t.join()
7.683s
19. Thread 1
Thread 2
Thread 3
I/O
release GIL Context Switch
acquire GIL
Python은, I/O(/System call)이 발생하게 되면,
thread는 GIL을 해제하게 되고, 다른 GIL을 획득 하는
thread로 제어권이 넘어갑니다.
20. Thread 1
Thread 2
Thread 3
I/O
release GIL Context Switch
acquire GIL
Python은, I/O와 같은 경우 처럼 Python 객체를
건드리지 않고, 계속 수행할 필요가 있을 경우
Py_BEGIN_ALLOW_THREADS 를 통해서, 계속
thread를 수행하게 할 수 있습니다.
21. 또한, system call뿐만 아니라, Python byte code
상에서 100개가 실행될 때마다도 마찬가지로 GIL을
해제함으로써, 다른 thread로 제어권을 넘기게 됩니다.
Thread 1
Thread 2
Thread 3
I/O 100 tick
release GIL C/S
acquire GIL
22. Python은 사실상
Single Thread
따라서, Python은 threading 모듈을 통해서 여러 thread
를 생성할 수 있음에도 불구하고, 사실상 GIL에 의해서 예
외적인 경우를 제외하고, 한 시점에는 하나의 thread만 작
동하는 것 처럼 보이게 됩니다.
26. Thread 1
Thread 2
Thread 3
I/O I/O
C/S ?
System call에 의해서 GIL을 획득하지 못한 thread가 계속 수행되는
경우에도, system call이 종료되고 python 코드를 수행하기 위해서는
GIL을 획득해야하기 때문에, 원하는 시점에 thread가 제어권을 다시 획
득하는 보장이 없습니다.
33. single thread
성능에 우위
Python은 memory 관리를 reference counting의 방법으로 하는데,
GIL에 대비되는 fine-grained lock방법을 사용할 경우 모든 객체의 접
근마다 lock 으로 관리해야 하므로 그 overhead가 매우 큽니다.
56. “(…) If you want your application to make
better use of the computational
resources of multi-core machines, you
are advised to use multiprocessing. (…)”
https://docs.python.org/2/library/threading.html
60. if __name__ == '__main__':
processes = []
for i in xrange(2):
processes.append(multiprocessing.Process(target=calc))
processes[-1].start()
p.join() 2.431s
for p in processes:
61. multiprocessing 모듈은, fork를 통해서 동시에 여러
프로세스에 원하는 작업을 실행할 수 있도록 도와주는
모듈입니다. 앞서 예제 코드를 실행할 경우, main
process를 포함해서 추가로 2개의 프로세스가 작동하
여 병렬화 되는 모습을 확인할 수 있습니다.
66. from multiprocessing
import cpu_count, Pool
!
if __name__ == '__main__':
pool = Pool(processes=cpu_count())
for url in ('http://deview.kr',
'http://beatpacking.com',
'http://kkung.net'):
pool.apply_async(fetch, url)
Pool 객체는, 마치 thread pool과 같이 지정한 갯수의 프
로세스들에 작업을 지속적으로 할당하여, 병렬처리를 손쉽
게 할 수 있도록 지원합니다.
75. connect('deview.kr', function(result, socket) {
socket.read(function(data) {
socket.write(data, function(result) {
socket.close(function(result) {
});
});
});
});
또한, 일반적으로 비동기 방식으로 I/O를 다루는 방법은
동기적으로 I/O를 다루는 방법보다 개발의 복잡도를 높
이고, 코드의 구조를 알아보기 어렵게 합니다.
133. while True:
events = wait_for_events()
for event in events:
handle_event(event)
event loop란, 위 코드와 같이 event가 발생하길 기다렸다가, event가
발생한다면 적절한 event 처리기를 실행하는 구조를 말합니다.
148. def hello():
while True:
name = (yield)
print 'Hello %s' % name
!
c = hello()
c.next()
c.send('kkung')
c.send('Deview 2014')
!
Hello kkung
Hello Deview 2014
python의 generator를 이용한 coroutine 예제 입니다. hello() 함수는 무한
루프를 도는 함수임에도 불구하고, 예제와 같이 출력되는 모습을 볼 수 있습니다.
149. def hello():
while True:
name = (yield)
print 'Hello %s' % name
!
c = hello()
c.next()
c.send('kkung')
c.send('Deview 2014')
!
Hello kkung
Hello Deview 2014
hello 함수는 내부적으로 무한 루프를 돌도록 되어 있으나 yield를 포함하고 있어
generator 함수로 처리되며, c.next()에 의해서 진입합니다.
150. def hello():
while True:
name = (yield)
print 'Hello %s' % name
!
c = hello()
c.next()
c.send('kkung')
c.send('Deview 2014')
!
Hello kkung
Hello Deview 2014
yield에 의해 문맥은 다시 hello() 함수 바깥으로 전환되고, c.send(‘kkung’) 에
의해서 ‘kkung’이 전달됨과 동시에, 문맥이 hello 함수로 전환됩니다.
151. def hello():
while True:
name = (yield)
print 'Hello %s' % name
!
c = hello()
c.next()
c.send('kkung')
c.send('Deview 2014')
!
Hello kkung
Hello Deview 2014
hello 함수는, 문맥이 전환됨에 따라(재진입) print문을 실행하게 됩니다.
158. MAIN THREAD
Coroutine
Coroutine
Coroutine
Coroutine
앞서 설명했던 바와 같이 GIL은 시스템의 threading library의 lock
mechanism에 의존적이기 때문에 문맥 전환이 암시적입니다. 그러나
greenlet은 그림과 같이 명시적으로 문맥을 전환시킵니다.
160. 너무 많은 일을 하면 X
greenlet은, 앞서 설명한바와 같이 특정 thread에서 귀속되며 그
thread단위로 스케쥴링 됩니다. 이는 특정 greenlet이 CPU를 오
래 점유하면 할 수록, 같은 thread에 있는 다른 greenlet들은 idle
상태를 유지한다는 말과 같습니다.
161. Sync-like API
(NO CALLBACK!)
gevent의 장점중 한가지는, Python에 존재하는 표준 library의 인
터페이스를 그대로 변화 없이 사용해도, 비동기적인 처리 효과를 제
공해 준다는 점입니다.
163. connect('deview.kr', function(result, socket) {
socket.read(function(data) {
socket.write(data, function(result) {
socket.close(function(result) {
});
});
});
});
예를들어, 특정 언어에서는 비동기 처리를 위해서 callback을 연쇄
적으로 제공하는 방법(CPS)외에는 선택지가 없는 경우도 있지만….
164. s.connect('deview.kr')
data = s.read()
s.write(data)
s.close()
gevent를 사용한다면, Python에서는 이렇게 직관적으로도 비동기
처리를 할 수 있습니다. (특정 언어 비하의 의도는 없습니다^^;)
166. def recv(self, *args):
while True:
try:
return sock.recv(*args)
except error as ex:
if ex.args[0] != EWOULDBLOCK:
raise
self._wait(self._read_event)
167. def recv(self, *args):
while True:
try:
return sock.recv(*args)
except error as ex:
if ex.args[0] != EWOULDBLOCK:
raise
self._wait(self._read_event)
168. def recv(self, *args):
while True:
try:
return sock.recv(*args)
except error as ex:
if ex.args[0] != EWOULDBLOCK:
raise
self._wait(self._read_event)
이것이 가능한 이유는, gevent는 내부적으로 python library의 인터페이스를 그대로
구현하면서, 각 API를 모두 greenlet에서 수행하기 때문입니다. Non block socket을
이용해서, 요청한 순간 바로 처리할 수 없다면 event-loop에서 처리 가능해질때
greenlet을 깨우도록 기다립니다.
171. import asyncio
!
@asyncio.coroutine
def compute(x, y):
print(Compute %s + %s ... % (x, y))
yield from asyncio.sleep(1.0)
return x + y
!
@asyncio.coroutine
def print_sum(x, y):
result = yield from compute(x, y)
print(%s + %s = %s % (x, y, result))
!
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
Python 3.4 이상부터는, AsyncIO 라이브러리를 이용할 수도 있습니다. 이 라이브러리
는, python 3.3에서 도입된 yield from 문법을 이용해서 coroutine을 구현하여 좀 더
pythonic한 방법으로 비동기 처리를 제공합니다.
172. import asyncio
!
@asyncio.coroutine
def compute(x, y):
print(Compute %s + %s ... % (x, y))
yield from asyncio.sleep(1.0)
return x + y
!
@asyncio.coroutine
def print_sum(x, y):
result = yield from compute(x, y)
print(%s + %s = %s % (x, y, result))
!
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
173. import asyncio
!
@asyncio.coroutine
def compute(x, y):
print(Compute %s + %s ... % (x, y))
yield from asyncio.sleep(1.0)
return x + y
!
@asyncio.coroutine
def print_sum(x, y):
result = yield from compute(x, y)
print(%s + %s = %s % (x, y, result))
!
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
174. import asyncio
!
@asyncio.coroutine
def compute(x, y):
print(Compute %s + %s ... % (x, y))
yield from asyncio.sleep(1.0)
return x + y
!
@asyncio.coroutine
def print_sum(x, y):
result = yield from compute(x, y)
print(%s + %s = %s % (x, y, result))
!
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
175. 앞서, coroutine은 진입점이 여러개인 함수다 라는 표현을 사용하였는데, 위 코드에서
보는 바와 같이 print_sum, compute, asyncio.sleep 이 각 호출되면서도 종료시에
yield from의 다음 줄이 수행되는 모습을 볼 수 있습니다.
178. from gevent import monkey
monkey.patch_all()
monkey patch는, python 표준 라이브러리들 중 IO에 관계 있는 주요 라이브러리들
을, gevent의 그것으로 대체시킵니다. 따라서, monkey patch후에는 기존의 python
에서 IO를 다루던 방법 그래도 수행하여도, gevent에 의해서 비동기 처리로 전환됩니다.