Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Backup.

  • Loading branch information...
commit 6553a92a5532f2fb7124ae02172522e575662f29 1 parent 97ea6ec
@dahlia authored
Showing with 26 additions and 0 deletions.
  1. +1 −0  18355002657-i-o.json
  2. +25 −0 18355002657-i-o.md
View
1  18355002657-i-o.json
@@ -0,0 +1 @@
+{"date-gmt": "2012-02-27 01:53:00 GMT", "url": "http://blog.dahlia.kr/post/18355002657", "url-with-slug": "http://blog.dahlia.kr/post/18355002657", "format": "markdown", "-slug": "-i-o", "id": "18355002657", "unix-timestamp": "1330307580", "date": "Mon, 27 Feb 2012 10:53:00", "type": "regular", "slug": "i-o", "tags": ["async io"], "reblog-key": "DaRnBDii"}
View
25 18355002657-i-o.md
@@ -0,0 +1,25 @@
+최근 비동기 I/O 관련해서 이런 저런 글을 썼는데 간단히 나의 생각을 정리해보고자 한다.
+
+ - 이상적인 방식은 이렇다: 이벤트 멀티플렉싱(요즘에는 epoll, kqueue 직접 안쓰고 보통 libev, libevent 같은 걸 쓴다) + 쓰레드 풀링 + 넌블럭 API.
+
+ - 이를 위한 이상적인 추상화는 렉시컬 스코핑(lexical scoping)으로 정지되기 직전 문맥을 사용하는 CPS가 아니라 코루틴(coroutine)으로 문맥을 자연스럽게 이어나가는 방식이다. 후자가 되면 전자처럼도 쓸 수 있다.
+
+ - CPS가 안 좋은 이유는 가독성 때문이 아니라 복잡도 때문이다. 예를 들어 어떤 RPC API가 있는데 한번에 정해진 갯수만 가져올 수 있고, 전체를 가져오기 위해서는 확정 불가능한 수만큼 요청해야 한다고 가정해보자. 이를테면 친구 목록을 가져오는 API인데 매번 다음 페이지 토큰이 포함된 URL을 요청해야 한다. 코루틴을 쓰면 그냥 루프(`while` 문 등)를 돌리면 된다. CPS로는? 이름 있는 함수 하나 만들어서 C에서 꼬리 재귀하던 식으로 짜면 된다. 어느 쪽 코드가 의도가 훨씬 잘 드러날까?
+
+ - 연산 속도는 비동기 I/O와 상관 없는 부분이다. 관련 논의에서 연산 속도 얘기를 하는 것은 붕어빵 얘기하는데 “근데 그건 얼큰한 맛이 없잖아요”라고 딴죽거는 것만큼 이상하다. (물론 얼큰한 붕어빵을 원할 수는 있지…) 비동기 I/O는 어쨌거나 동시에 처리할 수 있는 채널을 “넓히려는데” 있지 속도를 올리는 것과는 다른 문제이다. 이른바 [C10k 문제][1]라고 한다.
+
+ - 현존하는 방식 중에 가장 이상적인 것들을 몇개 꼽자면 다음과 같다: Erlang, Go, Haskell[^1], Python + gevent (혹은 eventlet). 이상적인 I/O 프로그래밍 모델에 이를 위한 이상적인 언어적인 추상화를 갖췄기 때문이다. 예를 들어 `from gevent.monkey import patch_all; patch_all()` 한 뒤에 그냥 블럭킹 API를 쓰면 내부적으로는 libev 위에서 이벤트 멀티플렉싱된다. 투명하다.
+
+ - Twisted, node.js, EventMachine은 CPS를 쓰지만 렉시컬 스코핑(lexical scoping)의 덕을 보므로 아주 어려운 것은 아니다. 하지만 가장 이상적인 방식은 아니다. I/O 프로그래밍 모델은 이상적이지만, 언어적인 추상화 부분에서는 타협한 결과라고 생각한다. 개인적으로 Ruby는 멍키패칭도 되고 컨티뉴에이션도 있고 파이버(fiber)도 있으니 gevent 같은게 하나 나올 때가 된 것 같은데 아무도 그런걸 만들 생각을 안하는 것 같아서 아쉽다.
+
+ - Nginx 같은 경우 C에서 straightforward로 C10k 문제를 해결했다. 결과적으로 좋은 제품이 나왔지만 개인적으로는 코드 복잡도가 꽤나 높아졌다고 본다. “중지된 문맥을 복구하는 일”을 언어의 도움 없이 직접 구현하는 것이 얼마나 힘든지는 Nginx 모듈 작성을 시도해보면 느낄 수 있다.
+
+마지막으로 아주 일반적인 얘기지만, 가끔 기술이 분명 발전했는데도 불구하고 “본질적으로는 발전하지 않았으며 결국 예전부터 쓰이던 패턴과 같을 뿐이다”라고 주장하는 무리들을 종종 보게 된다. 뭐 어떤 관점에서는 맞는 얘기라고 생각한다. 모든 프로그램은 튜링 완전한 언어 위에서 루틴을 엮는다는 점에서는 동일하다. 따라서 기계어로 코딩하던 시절과 지금은 본질적으로 달라진 게 없다. 자, 납득 가능하신가?
+
+그럼 그때와 지금이 달라진 것은 무엇일까? 바로 복잡도를 얼만큼 제어하느냐다. 그리고 이게 기술의 가장 큰 요소이다. (그리고 프로그래밍 언어 애호가로서, 프로그래밍 언어가 패턴을 없애는 기능들을 추가해야 하는 이유이기도 하다고 주장하고 싶다.)
+
+[^1]: 정확히는 GHC. [모든 I/O는 해당 플랫폼의 이벤트 API 위에서 멀티플렉싱되게 바이너리가 나온다.][2] gevent이 달성하려는 투명한 비동기 I/O 프로그래밍을 해킹(멍키패칭)이 아닌 컴파일러 구현으로 제대로 해결했다고 볼 수 있다.
+
+*[CPS]: Continuation-Passing Style
+[1]: http://en.wikipedia.org/wiki/C10k_problem
+[2]: http://www.haskell.org/haskellwiki/FAQ#How_do_I_do_event-based_IO_in_GHC_Haskell.3F__Should_I_call_select.2C_epoll.2C_etc.3F
Please sign in to comment.
Something went wrong with that request. Please try again.