node.js 혹은 CPS 단상

지난 주에 둘째 태어나서 병원에 있는 동안 SNS와 블로고스피어를 스쳐지나간 글 중 제일 눈에 띄는 부분이 “CPS와 node.js” 관련된 내용이었다.

iPhone은 정리에는 그다지 좋은 기기라고 못하겠다. 내가 생각하던 문맥을 저장하기엔 내 활용 방법의 문제거나 기기가 부족하거나; 여하튼 기억나는 글들을 여기 정리하자면 (twitter는 정리가 불가능하니 제외),

웹 서버 아키텍처와 프로그래밍 모델의 진화 http://ricanet.com/new/view.php?id=blog/110903 (node.js의 I/O 모델을 점검하기에 좋은 글)

Node.js의 소개글 들에 대한 유감 http://himskim.egloos.com/3810574

이에 대한 홍민희 님의 글 http://blog.dahlia.kr/post/18300740247

그리고 다시 (홍민희 님의) 비동기 I/O 프로그래밍 모델 관련 글 http://blog.dahlia.kr/post/18355002657

 

이에 대한 내 생각을 정리하자면:

1. node.js는 hype이라 생각한다. node.js 광신도(…)들이 말하는 장점은 이미 오래된 얘기고 (이벤트 기반의 비동기 I/O 모델, 단일 이벤트 루프와 그로 인한 싱글 스레드의 단순함), 이런 시도를 하는게 node.js만 있는 것도 아니다. 다만 세상에 JavaScript 프로그래머가 많기 때문에 그렇게 느껴지는 것 뿐이지. 서버 혹은 백엔드 프로그래밍에선 너무 오래된 — (게임) 서버 프로그래밍을 주로 하던 이들 사이엔 이게 왜 이슈가 되는지 궁금해하는 사람들이 나온다 — 이슈라서 말이지.

다음 언어들로도 `흔히’ CPS 스타일의 프로그래밍을 할 수 있다. 그리고 이 언어들 모두에서 node.js에서 `하면 안된다’라고 주장하는 한 이벤트에서 긴 계산을 하는 일도 실행 모델에 따라선 해도 된다. 그리고 이 언어들에선 `흔히 하는 일’에 불과하다.

Go: goroutine 자체가 일종의 CPS 스타일 프로그래밍이다. 그리고 일반적인 이벤트 큐 처럼 쓸 수 있는 channel 개념 역시 존재한다. 그리고 이 두 개념을 최대한 활용해서 만든 언어가 Go다. 자세한 내용은 내 과거 포스팅을 참조하자 (https://rein.kr/blog/archives/tags/go) 개인적으론 이런 류의 I/O 비동기 / 멀티플렉싱을 생각하고 짠다면 Go가 가장 깔끔한 프로그램이 나온다고 생각한다.[1]

C#: C#의 익명 함수 (delegate라고 부르던데) 역시 이런 CPS 스타일로 프로그래밍 할 수 있다. Nexon의 `마비노기 영웅전’과 `마비노기 2’도 이런 개념을 활용해서 서버를 구현했다. 2011년 NDC 세션의 내용을 정리해놓은 것처럼 (https://rein.kr/blog/archives/2696https://rein.kr/blog/archives/2671) 이걸가지고도 JS에서 흔히 하듯이 성공/실패 처리를 cps로 작성한다.

C++: C++11의 lambda 혹은 그 이전의 std::function[2] 을 이용하면 역시 CPS 형태의 코드를 작성할 수 있다. 내가 작성한 게임 서버 라이브러리 역시 이를 이용한 CPS 스타일의 코드가 포함되어 있다.

 

2. node.js가 단일 스레드, 즉 단일 문맥만으로 처리하기 때문에, computation-intensive한 작업은 처리할 수 없다. 그래서 어쩔 수 없이 백그라운드 태스크 큐 등을 써야하고, 이로 인해 생기는 지연 시간(latency) 문제는 극복할 수 없다.
게임 서버에서도 node.js처럼 (혹은 그보다 낫게)

  • 복수의 스레드가 네트워크 이벤트(read/write)를 비동기로 처리하고,
  • 네트워크 이벤트를 서버 메인 스레드(그러니까 논리적으론 단일 스레드 프로그램)로 전달하고,
  • 서버 메인스레드가 내부 로직을 처리해서 다시 네트워크로 보내는

구조를 사용한 서버들이 있다. 다만 이 경우엔 계산 량이 많은 경우 지연 시간이 길어진다. 그것도 게임 상에서 상호 연관이 없어 보이는 부분들 사이에 영향을 주는 형태로 -_-;
게임 서버는 웹과는 좀 다르게 persistent storage에 다 저장하지도 못하고, 각 연결 간에 상호 작용도 많아서 어쩔 수 없이 생기는 문제이긴하지만 맨 첫 글에서 얘기하는 것 처럼 node.js가 `유니크’한 것은 아니다. JavaScript로 서버 프로그래밍을 진행한다는 것은 꽤 참신하지만 말이다.

그리고 서버 메인스레드가 1개가 아니라 복수개 일 수도 있고 — 내가 본 코드에선 이게 주류다 — 걔 중에도 IO 스레드가 full swing을 하돼 non-blocking한 알고리즘을 이용해서 복수의 큐를 사용하고, 블럭킹을 제거한 구현체도 있다.

 

3. 프로그래밍 모델은 CPS는 `쉽게 만들어낼 수 있지만’, 프로그래머에겐 꽤 골치거리라 생각한다. 홍민희 님의 말대로 가장 쉬운 형태는 co-routine일 것이라 생각한다. CPS처럼 이전 문맥에 대해 생각할 필요도 없고, 불필요한 nesting (node.js말하는 거임) 도 필요치 않다. 다만 이 경우에는 언어적인 수준에서 지원하지 않으면 안된다. 불행히도 내가 사용하는 주요 언어인 C/C++의 경우 co-routine을 사용할 수단은 OS fiber수준 정돈데, 이 경우엔 다른 동기화 객체들을 쓸 때, 동기화 단위가 스레드라서 fiber끼리 switching할 때 지옥을 보게 된다. 하지만 node.js 정도의 프로그래밍 모델에선 이게 가장 간편할 것이다. 각 연결 별 문맥끼리 상호작용이 적다면 더더욱 말이다.

  1. 이 언어를 만든 회사가 Google이란 생각을 하면 더 그렇다 []
  2. 혹은 그 이전 구현체인 std::tr1::function, 혹은 이거랑 별반 다를게 없는 boost::function을 생각하면 더 이전부터 []

2011, 내 프로그래밍 언어는…

twitter에 #code2011 에 덧붙여 글 하나 쓰기. 2011년에 주로 사용한 언어는,

  • C++
  • Python
  • C#
  • JavaScript
  • SQL
  • Lua
  • Go
순으로 쓴 것 같다. (트윗에는 Lua를 못 적었음…)

C++

우선 주 언어로 쓴 C++. 내년에 공개될 모 서버를 작성했다. 사내 테스트까지 나간 것 중 C++11을 사용한 것으론 처음일듯.
그리고 옮겨간 팀의 서버 역시 C++로 작성하고 있다. 크래시 보고 도구도 밑 단은 C++…

내가 주목한 특징을 꼽자면,

  • 바위에 끌로 새기는 것 같이 작업함 – 집중해서 빡쎄게;
  • lambda로 이전에 boost 쓰는 걸 대체 – C++11 만세!(…)
  • 괜찮은 성능 – 내가 사용하는 모든 언어 중 가장 월등한 성능을 자랑한다
  • Bare metal 위에서 돈다 (위랑 같은 의미?)
  • 원하는 수준만큼 저 수준에서, 원하는 만큼 세밀하게 제어할 수 있다
그래서 이걸로 여전히 게임 서버를 짜고 있는듯…
 

Python

팀 내의 코드 생성기나 대부분의 스크립트는 python으로 작성한다. 올해에는 대략,

  • 데이터 변환 도구; .xls -> sqlite -> (C++ internal form) 으로 몇 가지 데이터를 옮겼는데, 이 중 첫 단계는 모두 python에서 해결
  • 크래시 덤프 분석 및 집계 기능. 크래시 덤프를  cdb 수준에서 분석하면 이를 토대로 다른 덤프들과 비교하고, 이에 대해서 웹에서 볼 수 있게 했다
  • 덤프 수집 (web)
  • 크래시 덤프 뷰어 (web)
  • 빌드 도구나 덤프 관련 툴에서 메일 생성 / 알림 기능

에서 python을 주로 썼다. 물론 예전부터 쓰던 코드 생성기, 빌드 보조 도구에도 python을 쓰고 있다.

…덧붙이자면 요즘 .svg를 python으로 만드는 뻘짓을 했다. 근데 생각해보니 python-cairo를 쓰는 게 더 간단하잖아?;;;

 

C#

연초에 파일 전송 도구를 하나 짜야 했다. 원래 생각했던 건 python 기반으로 빨리 짜고 딴거 하고 놀자(…)였는데, 

이거 누가 유지 보수 하냐

라는 태클에 물건너 갔음. 흑흑.

그래서 실 서비스에서 써본적(…)도 없는 C#으로 짰음. 사실 태클건 사람들은 태클 걸면 C++로 짤 거라고 생각했다고…
애매하게 편하고, 애매한 수준까지만 제어할 수 있어서 개인적으로 불만이 좀 많았음. 내가 python을 안 쓴다면 모를까 Orz. 

누가 비슷한거 시키면 그땐 정말 python 쓸 테다… 일단 저거 linux에서 옮겨서 쓸 생각을 하니 눈 앞이 캄캄하기도 하고 — 누가 Mono 관련 조언 좀…

 

JavaScript

까놓고 말하면 js에는 완전 초보임. 그래서 python쪽에서 js를 쓰긴 했는데, 크롬에서만 돌길래(…) 포기하고 jQuery로 전부 옮겼다. JavaScript를 썼다기 보단 jQuery를 쓴 느낌이지만 (…).

CouchDB에서 map-reduce 용 언어로 js를 쓰긴하지만, 이건 그렇게까지 복잡하게 활용한게 없으니 좀 애매하다. 따지고 보면 올해는 js보다 json을 더 많이 본 것도 같다(운다).

올해 얻은 건 jQuery를 좀 더 익숙하게 쓰게 되었다 정도?

 

SQL

ORM하나 제대로 쓰고 싶었습니다만 꿈도 희망도 없어; 그냥 python위에서 클래스 수준으로 포장해서 하단의 데이터 스토어로만 쓴 듯.
위안이라면 MySQL / CouchDB를 선택해서 쓸 수 있게 만들었다 정도지만; 

Lua

팀 내에서 사용하는 서버 용 부하 테스트 툴은 (NDC에서 ipkn이 발표한 내용은 http://ipkn.upnl.org/ndc.html 을 참조) lua를 스크립트 언어로 쓴다.
그런 의미에서랑, 몇 가지 도구에서 lua를 썼다. 이쪽에선 내가 발전이 전혀 없는듯… 

Go

연초에는 좀 만졌지만 — 위에서 C#/Python으로 헀던 일 중 일부는 원래 Go로 하려던 건데 — 그 이후엔 망했음.
과연 내년에는 나의 야망(…)대로 Go로 돌아가는 서비스를 만들 수 있을까;

일단 올해에는 회사 안에서 / 실제 서비스에서 linux 기반으로 해도 된다는 건 확인했으니, 내년을 기약합시다(…). 

 

요약:

  • C++/python을 여전히 많이 쓰고 있다.
  • C#은 안 편했음; 내년에는 아마 다른 선택을 할 듯.
  • 웹 질(…)로 JavaScript/SQL을 좀 만졌다. 내년엔 기약 없음…
  • Go. linux 기반 서비스를 좀 더 해도 될 것 같으니 내년을 기약 

Gccgo in GCC mainline

Go 프로그래밍 언어는 크게 두 종의 컴파일러를 가지고 있다. 하나는 gc라고 통칭해 부르는 플랫폼 별 8g, 6g, 5g, … 등의 컴파일러고, 다른 하나는 GCC의 프런트 엔드인 Gccgo 라는 컴파일러다. 이 두 개는 크게 몇 가지 차이를 갖고 있다.[1]

  • gccgo와 gc가 goroutine 을 다루는 방식이 상이하다. gccgo는 goroutine당 1개의 OS 수준 thread를 써야 한다 반대로 gc는 적정 수(runtime.GO_MAX_PROCS( ))의 OS 수준 스레드 위에 goroutine들을 multiplexing한다.
  • gccgo는 GCC에서 만드는 linkage들을 활용할 수 있다 – C 뿐만 아니라 C++링크도 가능하다; 반면에 gc는 cgo를 통한 C 언어 모듈들만 링크할 수 있다. 물론 이것도 linkage 핵을 잘 쓰면 C++이 될 것 같지만…
  • gccgo와 gcc는 서로 다른 백엔드를 쓴다. 이에 따라 런타임도 다르고(이거 때문에 언급한 첫 번째 차이가 생긴다), 컴파일러가 최적화 해주는 수준이 다르다. GCC를 백엔드로 쓰는 gccgo가 최적화가 더 잘 이루어지지만, goroutine관련해서는 gc가 더 나은 구현을 하기 때문에, 응용에 따라 성능이 더 잘 나오는 쪽이 다르다.

 

Ian Lance Taylor의 블로그 포스팅(Gccgo in GCC)을 보고 확인해보니, 이 중 Gccgo가 드디어 GCC mainline에 포함되었다 – trunk 에서 gcc.c 등에 해당 스텁이 추가된 상태다. 다음은 해당 commit log,

r167407 | ian | 2010-12-03 13:34:57 +0900 (2010-12-03, 금) | 22 개의 행

Add Go frontend, libgo library, and Go testsuite.

gcc/:
        * gcc.c (default_compilers): Add entry for ".go".
        * common.opt: Add -static-libgo as a driver option.
        * doc/install.texi (Configuration): Mention libgo as an option for
        --enable-shared.  Mention go as an option for --enable-languages.
        * doc/invoke.texi (Overall Options): Mention .go as a file name
        suffix.  Mention go as a -x option.
        * doc/frontends.texi (G++ and GCC): Mention Go as a supported
        language.
        * doc/sourcebuild.texi (Top Level): Mention libgo.
        * doc/standards.texi (Standards): Add section on Go language.
        Move references for other languages into their own section.
        * doc/contrib.texi (Contributors): Mention that I contributed the
        Go frontend.
gcc/testsuite/:
        * lib/go.exp: New file.
        * lib/go-dg.exp: New file.
        * lib/go-torture.exp: New file.
        * lib/target-supports.exp (check_compile): Match // Go.

이제 다음 GCC release (4.6; 내년 초 예정) 가 설치된 시스템에서는 – 일단 GNU/linux 한정; 가능한 아키텍처는 정확히 모르겠지만? – go 프로그래밍 언어를 그냥 사용할 수 있을 듯. go 소스 트리 hg로 받아다가, 빌드하고, 사용하는 환경에 비하면야 훨씬 더 많은 수의 프로그래머들이 새 언어를 접해보고, 시험해 볼 기회를 갖게 될 듯 하다 – 더불어, 많은 수의 프로그래머가 사용하는 환경만 갖춰지면, 더 많은 기여가 이루어질 듯 하니 이 쪽이 더 좋은(?) 일인 듯 하다..

그런 점에서 내년 초가 기대 된다. 안 그래도 GCC 4.6에는 C++ 0x의 range 기반 for 문과 nullptr가 추가된단 말이다(…).

  1. http://golang.org/doc/go_faq.html#Do_Go_programs_link_with_Cpp_programs http://www.airs.com/blog/archives/448 , 그리고 이 블로그의 예전 Go 관련 포스팅을 참조 및 요약 []

팀 세미나: Go Programming Language #4

어쩌다보니 세 번째 했던 건 안 올려놨다는 사실을 확인. 일단 그 내용은 대부분 지난 번 성능 평가 관련 글의 내용이라 일단 나중에 기회 되면 올리기로…

이번에는 Go 를 가지고 UDP 데이터그램을 적당히 변조해주는 프로그램인 troll을 재 구현하고, 그에 대한 기록을 남겨봤다.

Introduction to Go Programming Language #4

Google Go: 간단한 성능 평가

수요일에 있을 팀 세미나 준비하면서, Google Go로 간단한 병렬 프로그램을 짜고, 직렬(serial;sequential) 구현과 성능을 비교 해봤다. 일단 간단히 여기에 정리. 모든 테스트는 x64 버젼의 MacOSX 를 돌리는 MacBook(2008년에 산 녀석)/dual-core 에서 이루어 졌다.

매우 간단하게 병렬화 되는 알고리즘인, quicksort를 가지고 성능 테스트를 했다. pivot을 그냥 순열 중간에서 찍어내는 단순 무식한 방법으로 만들었음.

이하에선 대략 배열 길이가 512 보다 작으면 그냥 순차 알고리즘만 동작하게 했음.

Naïve 하게 시작

우선 처음엔 fork-join 하는 형태로 좀 많이 naïve 하게 짜봤다: 정수 400만개 정렬 시켰더니, goroutine 수가 너무 많다고 뻗음. (이건 내 코드의 버그였음). 더 이상 fork하지 않고 도는 threshold를 좀 높게 잡고(8192?) 돌렸더니 대략 1200ms 정도 걸리더라. 단순하게 그냥 짠 알고리즘을 싱글 스레드로 돌리면 약 2000ms. 거의 2 배의 스피드업이 있긴하다.

그래도 이건 좀 아니다 싶어서(…), Work-stealing queue를 짜기 시작.

Work-Stealing Queue

완전히 다 구현한건 아니고, 배열을 둘로 쪼갠 순간 다음과 같이 동작하게 했다.

배열의 앞 부분은 work-stealing queue로 전달하고, 나머지는 그 goroutine이 직접 정렬하게 했다. 물론 이것도 재귀적으로 적용되니까 전부 한 goroutine에선 이루어지지 않는다.

Work-Stealing Queue를 간단하게 구현한다고(…) 좀 가짜로 만들었다. 일단 큐를 스레드 별로 두지 않았다. 다만 자기 자기 큐에서 꺼낼 때의 LIFO 동작을 흉내내려고 전부 큐에 넣는게 아니라 마지막에 분할한 부분 배열은 자기가 직접 정렬한다. 그리고 다른 스레드의 큐에서 꺼내는걸 따라(?)해서 큐에서 꺼내는건 FIFO…

처음에 작업 종료되는걸 감지할 방법을 못찾고 해매다가, Queue에 넣은 작업 갯수 만큼 종료 신호를 받게 했다. 이것도 단순히 빈 슬라이스를 응답으로 보내게 해서 이거 숫자를 셌다(…).

대략 1100ms 정도 걸린다. 총 goroutine 수가 입력에 비례하는게 아니라, 코어 수에 맞춰놔서 naïve 구현처럼 panic() 함수 호출되는 일은 없었음;;;

C++이랑 비교

C++의 <algorithm>에 있는 sort 랑 비교해봤더니 너무 큰 차이가 난다. 대략 700ms 좀 안 걸리게 돌더라. 이건 무려 싱글 스레든데;;; 사실 이건 내 구현이 sort() 함수의 introsort 보다 예상 성능 자체가 낮아서 생기는 일이기도 하지만;;
내가 짠 알고리즘을 싱글 스레드로 돌린 경우 1580ms 정도 걸렸다.  (cf. Go는 2000ms)

intel tbb의 tbb::parallel_sort()를 사용한 경우 대략 350ms 쯤 걸린다. linear-speedup? 이게 가장 빠르긴 하구나.

Go 쪽에 썼던 코드를 그대로 넣고 돌렸더니 대략 1600ms. 그래도 코어 2개 쓰는 쪽이 빠르긴 하지.

요약

Go(single-thread): 2000ms
Go (parallel) : 1100ms

C++(single-thread; same algorithm): 1600 ms
C++(single-thread; std::sort() ) : 700ms
C++(2-threads; tbb::parallel_sort() ):  350ms

간단한 감상: 좀 더 최적화 할 여지가 있어 보인다. 심심풀이로 실제 물리 코어 수 보다 더 많은 MACPROCS를 지정했더니 성능이 꽤 빨리 내려간다. 생각보다 channel 통한 통신의 오버헤드는 크지 않아서 만족스러웠다. fork-join 과 worker-thread 비슷하게 짠 게 큰 차이는 안나는걸 보니… 반대로 goroutine은 아무리 많이 만들어도(한 6만개 만들어도 잘 돈다) 괜찮긴 하더라..

+ naïve 하게 짜면 망한다(???).

팀 세미나: Go Programming Language #2

지난 번에 이어, 팀 세미나에서 얘기했던 내용을 업로드.

Introduction to Go Programming Language #2

거의 일주일 전에 한거지만 업로드는 이제야…

지난 번에 받은 느낌은 C버젼의 Erlang 비슷한 언어였는데, 좀 더 쓰다보니 대략 Python 을 연상케한다.

  • Syntactic sugar라거나, http 모듈 구현 방식이라거나, duck-typing이나 다름 없는 interface 구조라거나
  • 하지만 python 을 컴파일 언어로 만들긴 힘드니, 상당한 제약이 들어가 있다
  • 그럼에도 불구하고 꽤나 편하게 만질 수 있었다 (System Programming 언어를 표방하는 애들 중엔 제일…)
  • C 랑 매우 잘 붙는 것도 거의(…)
  • C 와의 인터페이스로 쓰는 C pseudo-package 도 왠지 ctypes가 생각 나는 것이…

…이런 느낌?

이번엔 지난 번(4주 전)에 이어 간단한 웹 서버 모듈 짜고, 이걸로 뭔가 보여주려고 했는데, 예제 짜고나서 이걸 설명하려니 지난 번에 설명하지 못했던 개념들이 이거저거 나와서(…) 그것들과 C 언어 링킹, Goroutine 과 그 구현의 현재 문제점들, 그리고 간단한 웹 서버 모듈을 설명하는 걸로 마쳤다.

일단 웹 서버 모듈(http 팩키지)만 놓고 보면, python 의 BasicHTTPServer 처럼 http 팩키지의 핸들러를 적당히 등록하는 방식으로 만들 수 있어서 꽤나 편했다. python이랑 달리 아예 URI 별로 핸들러를 등록시키는 방식이라, 상대적으로 모듈 쪼개기가 쉬워보임. C랑 잘붙어서 기존 라이브러리 가져다 쓰기도 매우 편한 것 같고… C랑 붙이기 위해서 “C” 란 가짜 패키지를 (만드는 척해서) 사용하는데, C.struct_name, C.function_name 만으로 접근되는게 꽤 편했음. (다만 이를 위해 SWIG 처럼 cgo 란 툴로 팩키지 빌드를 하긴 해야함)

여튼 C 연결을 쓰는 sqlite 래퍼를 찾고(…), 여기에 http 팩키지로 만든 웹서버를 띄워서 간단한 북마크 저장소를 시연하고 이번 치 세미나는 끝. 다음 번 주제를 좀 고민하고 있는데, 웹 서버처럼 상대적으로 full-swing 하는게 쉽게 보이는 걸 좀 더 해볼지, 아니면 게임 서버처럼 서로 얽히고 섥힌(…) 녀석을 만들어 볼지 고민 중이다.

Google Go on Android/nacl?

Google 의 Go Programming Language 의 빌드 상태 페이지를 발견하고, 적당히 살펴봤다.

Google Go 의 타겟 플랫폼으로 알고 있던 게, linux x86/x64, darwin(MacOSX) x86/x64, arm 이었는데, 몇 가지가 더 보이더라. 그 중 주목(?)할 만한 게 있었음.

일단 linux-arm-android 가 있고, 추가로 nacl 386 이 있다. 좀 놀란 이유는 며칠 전 구글 I/O 에서,

Android 지원은 가능하지만(arm 에서 빌드가 되니), 아직 높은 우선 순위는 아니다

라고 답한 거였는데 … (정확히는 FAQ 목록이지만) 그리고 이 빌드가 한참된거라서 (올해 1월부터 있다; Fail이 좀 많긴 하지만)

뭐 일단 Android 도 어느 정도 테스트되고 있는 듯(golang.org 에도 사용가능한 플랫폼으로 그에 대한 언급이 있다). 덤으로 WebApp 용 native client(nacl) 지원도 있는 듯 함. 다만 벤치 마크 페이지에는 darwin x86/x64, linux x86/x64 만 있는 걸로 보아 아직 불안정한 상태인 듯도. (그리고 Google I/O 발언대로라면 아직 그렇게 높은 우선 순위가 아니라는 정도?)

PS. 근데 내가 Go 를 정말로 쓰기 위한 최우선 과제(?) I/O 성능 개선인데, 이건 언제쯤? 사실 이건 밑 단 라이브러리만 잘 되면 좋겠는데. 하다 못해 Java 보다야 I/O 성능이 좋아야할 거 아닌가;;

이 글을 쓰는 사이에 스리슬쩍 FAQ관련된 비디오가 업데이트 되었다. 이건 집에가서 봐야하나.

팀 세미나: Go Programming Language #1

팀에서 매 주 한 번씩, (회사 일 이외에) 자기가 관심 갖는 주제에 대해 발표하는 시간을 가지고 있다.
그리고 그 시간에 먹을 피자 대금에 대한 사다리 타기 대결도(…)

나는 이 시간에 Google 에서 만드는 오픈 소스 프로그래밍 언어인 Go 에 대해 하기로 했다. 요즘 심심풀이로 만지는 언어가 Go라서…

여기 그 때 썼던 자료를 게시한다. export를 잘못했는지 슬라이드 노트들은 없다(…).

Introduction to Go Programming Language

사용한지 기껏해야 한달 좀 넘었지만, 기본적인 구조는 C를 따르고, 여기에 몇 가지 제약을 가하고 기본 타입과 concurrency primitive 를 더해서,[1] Erlang 비슷한 언어가 되었다는 느낌.

그래서 이런 느낌을 가지고 Go 가 가지는 강점과 약점이라고 생각되는걸 간단히 요약하자면,

  • 굉장히 빠른 시스템 빌드 시간 – 대부분의 의존성(type, import,…)을 명시적으로 해결하기 때문에 빌드가 정말 빠르다
  • 간단하고 어느 정도 duck-typing이 돼서 조작하기 쉽다
  • 메시지 패싱에 기반하는 병행/병렬 프로그래밍에 특화되어 있다 – Erlang 과 유사함
  • C 와 달리 내장 타입이 좀 더 다양하다
  • UTF-8 내장 + range() 문을 사용하면 문자열을 byte 단위와 문자 단위(variable-length)로 모두 순회하기 쉽다
  • 문자열은 상수고, 배열도 그냥 복사된다. 이건 Erlang 에 대해 내가 쓴 글에서도 말했듯이, 변하지 않는 데이터가 병렬성에선 정말 중요하다.[2]

단점도 몇 가지,

  • 아직 툴 체인이 완성되지 않았다 – gccgo(gcc를 백엔드로 씀), 8g(x86 용), 6g(x64 용), 5g(arm 용) 등이 따로 논다. 특히 전자와 뒤의 3가지는 정말 따로 놈. 내장 기능의 수행방식도 약간 다른 수준이라.
  • 라이브러리가 아직 정말 빈약하다 –_-;
  • 툴 체인이 완성되지 않았다?랑 비슷한 얘기지만, 아직 충분히 최적화된 코드가 아닌 느낌. 몇몇 벤치 마크 결과가 그리 좋지 않다. (Java 보다도 느리다니…)

뭐 그래도 Erlang 보다는 많은 수의 (예비) 프로그래머가 있으니(C/C++/Java) 상대적으로 병렬 프로그래밍 작성할 때 쓰기엔 나은 듯도 하다. 다만 얼마나 빨리 성숙한 상태에 도달하느냐가 문제지만;

덧. (MacOSX 의) Exposé 의 Spaces를 이용해서 Keynote 프레젠테이션과 코드를 실행하는 터미널을 번갈아 가며 쓰려고 했다. 그런데 프레젠테이션 중엔 Ctrl + 화살표[3] 로 다른 스페이스로 못 넘어 가더라. 그래도 Command + Tab 으로 프로그램 선택이 바뀌면 스페이스 바뀌니 어찌 되긴 하더라.

  1. 물론 GC도 제약이라면 제약이지만 추가사항으로 치자 []
  2. 물론 배열을 복사하지 않고, 배열 전체 혹은 일부를 참조하는 slice란 문법을 지원한다 []
  3. Fn +인가? 손이 기억하는 거라 잘 모르겠다 []