까멜레오 오픈과 더불어 지금까지 예제로 만들어진 까멜레오 위젯을 몇 개 소개해 볼까 합니다. 1탄으로는 자막 검색 위젯입니다. 자막 검색이라고 하면 약간 혼란의 여지가 있는데, 곰플레이어가 제공하는 것처럼 자막이 없는 비디오의 자막을 자동으로 찾아주는 서비스는 아니고 자막 내에서 원하는 단어를 찾는 기능입니다.

사용자 삽입 이미지

요즘 웹에 글 올릴 때 특정 장면을 캡춰해서 이미지나 애니메이트 GIF로 올리는 경우가 많은데, 이 때 효율적으로 사용하실 수 있는 도구입니다. 위 그림은 자막 내에서 "호모"라는 단어를 찾아본 것입니다. 검색 결과를 클릭하면 해당 장면의 스냅샷과 함께 자막을 보실 수 있습니다. 초록색으로 크게 보이는 버튼을 누르시면 해당 장면부터 재생을 할 수도 있습니다.

일단은 가장 기본적인 기능만 구현되어 있는데, 자막 검색 관련해서 추가적인 아이디어 있으면 알려주세요.

까멜레오 오픈소스 준비.

Posted 2008. 4. 10. 15:51
제가 일하는 노매드커넥션에서 1년간 준비해오던 까멜레오 프로젝트를 이번주에 오픈소스로 릴리즈하기 위해 지금 열심히 다듬고 있는 중입니다. 원래 계획은 이번주 금요일인데, 지금 문서화 작업 정도에 따라서 토요일이나 일요일 정도로 연기될 수도 있을 것 같습니다.

블로그를 통해 몇 번 소개드린 적이 있는데 까멜레오는 미디어 어플리케이션을 쉽게 만들 수 있는 미디어 프레임워크입니다. 물론 일반 사용자에게는 까멜레오 미디어 플레이어를 제공하기도 하지만, 오픈 소스 까멜레오는 미디어 플레이어 보다는 프레임워크에 가깝습니다.

오픈소스 까멜레오의 모토는 "Your Custom Player"입니다. 까멜레오는 단순히 미디어 재생을 넘어서 멀티미디어를 활용한 다양한 어플리케이션을 쉽게 만들 수 있게 프레임워크를 제공합니다. 까멜레오는 여러가지 방식으로 개인화할 수 있는 데 현재 크게는 1) 위젯과 2) 채널을 통해서 나만의 미디어 플레이어를 만들 수 있도록 하고 있습니다.

사용자 삽입 이미지

위 스냅샷은 위젯들을 고를 수 있는 화면입니다. 비디오 위젯은 동영상을 보면서 즐길 수 있는 간단한 어플리케이션으로 생각하시면 됩니다. 까멜레오는 일반 개발자들이 이런 동영상 위젯을 쉽게 작성할 수 있는 플랫폼 역할을 한다고 생각하시면 됩니다.

예를 들어 다음 스냅샷처럼 돋보기 위젯은 화면의 일부 영역을 확대할 수 있습니다. 미디어 플레이어에 일부 장면을 확대하는 기능을 집어 넣기 위해 미디어 플레이어를 전부 다 만들 수는 없는 노릇입니다. 까멜레오를 활용하면 기본 인프라는 그대로 활용하고 내가 원하는 기능만 플러그인 형태로 쉽게 끼워넣을 수 있습니다.

사용자 삽입 이미지


채널은 미디어 콘텐트를 다운로드 혹은 재생할 수 있는 유통 경로를 말합니다. 주스트(Joost), 바벨검(Babelgum), 미로(Miro) 등 요즘 미디어 플레이어는 전통적인 동영상 재생 기능과 콘텐트 다운로드 기능을 합친 것이 특징입니다. 까멜레오는 채널 개념을 만들고 개발자들이 자신이 보고 싶은 채널(P2P, UCC 등)을 쉽게 추가할 수 있게 하는 것이 특징입니다.어차피 동영상 재생 기능 및 기본적인 기능은 똑같은데 채널 하나 추가하려고 새로운 미디어 플레이어를 만들어내는 수고를 줄이는 것이 목적입니다.

사용자 삽입 이미지


까멜레오를 확장하는 구체적인 방법이나 API는 오픈소스와 더불어 공개할 예정입니다. 지금 열심히 문서 작업 중에 있으니 나만의 미디어 플레이어를 만들어서 친구들에게 배포하고 싶으신 분들은 관심 가지고 지켜봐주시면 좋을 듯 합니다.


Da Vinci Machine

Posted 2008. 4. 3. 03:12
JVM에서 돌아가는 프로그래밍 언어가 무려 200개가 넘는다는 사실을 알고 있으세요? Programming Languages for the Java Virtual Machine 보시면 JVM 상에서 동작하는 언어의 목록이 나와있습니다.

OpenJDK에서는 다빈치 머신(Da Vinci Machine)이라는 프로젝트를 하고 있습니다. 자바 가상 머신(JVM)을 자바 뿐만 아니라 여러 언어에서 쉽게 사용할 수 있도록 개선하는 것을 목표로 하고 있습니다.

특히 Groovy, Jython, Jruby 등 JVM을 타겟으로 하는 스크립트 언어 사용이 늘면서 전략적으로 동적 스크립트 언어 지원을 가장 큰 목표로 삼고 있습니다.

Microsoft 2008 Lang.NET Symposium에서 발표한 New Languages on the JVM: Pain Points and Remedies를 보면 JVM에서 다중 언어를 지원하면서 겪은 문제점과 해결책들을 잘 요약해서 보여주고 있습니다.

동적 스크립트 언어 구현에서 가장 골머리를 앓는 부분이 동적 메서드 호출입니다. 파이썬이나 루비는 런타임에 존재하지 않는 함수를 호출할 수 있지만 자바는 컴파일할 때 다 확인을 합니다. 자바를 구현한 JVM은 동적으로 메서드를 호출할 방법이 없기 때문에 복잡한 트릭을 사용해야 합니다.

다빈치 머신은 JSR 292 Supporting Dynamically Typed Languages on the JavaTM Platform 와도 관련이 있는데, invokedynamic이라는 바이트코드 명령을 추가하자는 제안이 된 상태입니다. 2008년에 JVM 확장을 확정하고 자바 7에는 추가하려는 생각인 것 같은데 관련된 내용을 좀 더 파악해야겠습니다.

프로그래밍 언어 학회

Posted 2008. 4. 2. 01:28
프로그래밍 언어 관련 학회는 대표적으로 POPL와 PLDI가 있습니다. 둘 다 광범위한 주제를 다루고 있는데 POPL이 이론쪽으로 치우친 반면에 PLDI는 구현도 중시하는 학회입니다.

Principles of Programming Languages (POPL)

Programming Language Design and Implementation (PLDI)


객체지향 언어 학회들은 다음과 같습니다.

Object-Oriented Programming, Systems, Languages, and Applications (OOPSLA)

European Conference on Object-Oriented Programming (ECOOP)


Foundations of Object-Oriented Languages (FOOL)


비고: 이 글은 정보를 계속 갱신하도록 하겠습니다.

지난달 중순에 아마존에서 직접 주문한 보물들이 어제 도착했습니다.

Foundations of Object-Oriented Languages: Types and Semantics by Kim  B. Bruce

객체지향 언어 책입니다. 객체지향분석론이나 객체지향방법론이 아니라 객체지향 프로그래밍 언어의 이론적인 배경을 파헤치는 책으로 보시면 됩니다. 타입 이론, 람다 칼큘러스(lambda calculus)가 등장하고 SOOL(Simple Object-Oriented Language)이라는 간단한 객체지향 언어를 가정하고 이를 정형적으로 기술하면서 객체지향 언어의 핵심을 설명하고 있습니다.

Advanced Topics in Types and Programming Languages by Bejamin C. Pierece

PL 교과서로 유명한 Bejamin C. Pierce의 책입니다. 학교 복학하고 PL 수업 청강할 때 Types and Programming Languages by Benjamin C. Pierce를 교재로 썼는데 Advanced도 읽어보려고 마음만 먹고 있다가 이제야 구입했습니다. 이 책은 조금 더 깊이 있는 타입 이론을 다루는데, 특히 Dependent Types, Effect Types, Proof-Carrying Code 등의 주제에 관심을 가지고 볼 생각입니다. 후반부에 나오는 Reasoning 쪽은 아직 잘 모르겠고요.


통계 및 데이터 분석용 언어 R

Posted 2008. 3. 31. 21:06
리스프(Lisp), SML, 얼랑(Erlang), 헤스켈(Haskell) 등은 각자 특징은 다르지만 범용 함수형 언어라는 공통점이 있습니다. 이와 달리 몇 가지 한정된 영역에만 쓰이는 특수 목적용 함수 언어도 있습니다. 대표적인 예로 통계 및 데이터 분석용 언어이자 환경인 R입니다. 기능이 1:1로 대응되는 것은 아니지만 매트랩(Matlab)이 장악하고 있는 도메인(데이타 분석, 통계, 수학)을 다룰 수 있는 함수형 언어입니다.

R 언어 홈페이지에 있는 An Introduction to R을 보시면 R의 기본적인 특징과 장단점을 파악하실 수 있습니다. IBM dW에도 Statistical programming with R라는 제목으로 R 프로그래밍 언어 소개가 올라와 있습니다. Part 1. Dabbling with a wealth of statistical facilities 제목의 1부는 기본적인 특징과 데이터 타입을 다루고, Functional programming and data exploration라는 제목의 2부에서는 함수형 언어로서의 특징을 이야기하고 있습니다.

R은 데이터 분석용 언어인만큼 기본 데이터 타입으로 벡터(vector)를 지원하고, 각각의 원소에 셈을 할 수 있는 elementwise 연산자를 지원합니다.

> x <- c(10.4, 5.6, 3.1, 6.4, 21.7)
> 1/x

위 코드는 x에 벡터 (10.4, 5.6, 3.1, 6.4, 21.7)를 바인딩하는 예입니다. 1/x는 elementwise 나눗셈으로 각 원소를 1에 대해서 나눈 결과값을 돌려줍니다.

통계에 강한 만큼 통계에 사용되는 mean (평균), sd (표준편차) 등을 기본으로 제공합니다.

> mean(basement)            # Mean fails if we include unavailable data
[1] NA
> mean(basement, na.rm=TRUE)
[1] 18.87542
> sd(basement, na.rm=TRUE)      # Standard deviation must also exclude NA
[1] 2.472855
> cor(basement, livingroom, use="all.obs")   # All observations: no go
Error in cor(basement, livingroom, use = "all.obs") :
        missing observations in cov/cor
> cor(basement, livingroom, use="complete.obs")
[1] 0.9513366
> cor(outside, livingroom, use="complete.obs")
[1] 0.6446673

물론 계산 후에는 멋진 그래픽으로 보여주는 것도 중요하기 때문에 데이터 시각화(data visualization)도 R의 장점 중에 하나입니다. R로 뽑아낸 그래프의 예는 앞서 언급한 dW 글인 Part 1. Dabbling with a wealth of statistical facilities 를 보시면 됩니다.


디버거 없이 개발하기

Posted 2008. 3. 28. 20:09

전 개발할 때 디버거를 자주 사용하지 않는 편입니다. C/C++, 자바, 파이썬, 하스켈 등 개발 언어를 가리지 않고 디버거는 거의 사용하지 않습니다. 물론 디버거를 안 사용하는 게 무슨 개발 철학 같은 건 아닙니다. 직관적으로 디버거를 사용하는 편이 좋겠다는 생각이 들 때도 있고 프로그램이 C 코드 안에서 죽을 때는 디버거를 실행시켜서 메모리와 변수 값을 보기도 합니다.

하지만 디버거로 절대 잡기 힘든 멀티쓰레드 버그가 난무하고 메모리 보호도 없는 임베디드 시스템 개발로 개발을 시작하다보니 버그가 생겨도 디버거를 써야지라는 생각을 잘 안 하게 된 것 같습니다. 대신 저는 디버깅에 크게 2가지 방법을 사용합니다. (다른 개발자도 마찬가지겠지만요)

1) 콘솔 출력

언어마다 다르지만 결국 중요한 지점 몇 군데가 변수 값을 찍어보는 걸로 디버깅이 시작됩니다. printf, System.out.println, print가 디버깅 도구인 셈이죠. 간단한 버그들은 보통 원인이 짐작이 되기 때문에 중요 변수를 콘솔로 몇 번 출력해 보면 문제가 파악됩니다.


2) 코드 검토

멀티쓰레드 버그에 대해서는 가장 강력한 디버깅 방법이 머리 속으로 시뮬레이션하는 것입니다. 증상을 놓고 왜 이런 문제가 생겼을지 의심가는 여러 쓰레드를 놓고 생각해 보는 것이죠. 하루종일 책상에 앉아서 왜 이런 현상이 생길지 생각만 하다가 집에 간 날도 있습니다.


버그를 찾아내면 기쁘긴 하지만 디버깅 자체는 괴롭습니다. 처음부터 버그를 안 만드는 게 최선이겠죠.  하지만 나름대로 열심히 짠다고 해도 버그 없는 프로그램을 만들기란 너무 힘듭니다. 코드를 작성했는데 한 번에 컴파일되고 돌아가면 스스로 의심합니다. "그럴리가 없는데." 어릴 때는 그래도 머리가 잘 돌아가서 가끔 실행은 제대로 안 되더라도 컴파일 에러 없는 코드 정도는 만들어내곤 했는데 요즘은 그나마도 힘들어 그냥 포기하고 무슨 마약처럼 컴파일러에 의존(주기적으로 컴파일을 해보지 않으면 불안함)해 살고 있습니다.

버그는 사람이 만들지만 버그를 유도하는 것은 개발 환경입니다. 특히 프로그래밍 언어는 개발자가 얼마나 많은 버그를 만드는지를 결정하는 중요한 요소입니다. 함수 언어를 사용하면서 느낀 점 중에 하나는 적당히 컴파일되게만 짜면 다른 언어에 비해 버그가 현저히 적다는 사실입니다. 물론 강력한 정적 타입 시스템에 도움이 큰 셈이죠. 여기에 덧붙여 버그를 줄이는 프로그래밍 언어의 특징으로는 변조불가능(Immutability), 순수(Purity), 참조 투명성(Referential Transparency) 등이 있습니다. 세 가지 단어는 서로 다 관련이 있습니다.

하스켈을 비롯한 함수 언어 개발자들이 다른 개발자들과 차이를 보이는 점 중에 하나가 디버거를 거의 사용하지 않는다는 사실입니다. 물론 GHCi에 보면 디버거가 없는 건 아닙니다. 앞서 언급한 언어적 특성 때문에 하스켈 디버깅은 디버거를 통해 변수에 잘못된 값이 들어가 있는지 확인하는 일이 필요가 없습니다. (사실 변수라고 부를만한 것도 별로 없습니다.) 그저 함수 명세에 맞춰서 코드가 제대로 작성되었는지 테스트 값 몇 개만 넣어보면 됩니다.

특히 하스켈의 중요 특징의 하나인 참조 투명성은 테스트 혹은 디버깅에서 강력한 힘을 발휘합니다. 참조 투명성이란 하스켈의 함수가 수학적인 함수와 동일하게 같은 입력(인자)에 대해서는 언제나 같은 값을 리턴함을 의미합니다. 하스켈의 순수 함수는 전역 변수를 참조하거나 IO에 영향을 받지 않기 때문에 테스트나 디버깅을 위해 가짜 오브젝트(Mock Object) 등을 만들어내는 수고를 할 필요가 없습니다. 그저 함수의 명세(specification)만 테스트하면 됩니다. 이런 특성 때문에 하스켈의 테스트프레임워크 중 하나인 QuickCheck은 명세 기반 테스트라는 용어를 사용하고 있습니다.

나는 연장질을 잘 못한다고 생각하기 보다는 내 연장이 나쁜게 아닐까라는 생각의 전환이 필요합니다.

Parallelism and Concurrency 2

Posted 2008. 3. 28. 03:10
얼마 전에 Parallelism and Concurrency라는 글을 통해 동시성(concurrency)와 병렬(parallelism)의 정확한 정의에 대해서 의문을 제기했었는데 오늘 Simon Peyton Jones가 최근에 정리해서 내놓은 Tackling the Awkward Squad: monadic input/output, concurrency, exceptions, and foreign-language calls in Haskell을 읽다가 보다 명확한 정의를 발견했습니다.

28 페이지 Concurrency에 대한 항목을 보시면 두 용어의 정의가 나옵니다.

A parallel functional program uses multiple processors to gain performance. For example, it may be faster to evaluate e1 + e2 by evaluating e1 and e2 in parallel, and then add the results. Parallelism has no semantic impact at all: the meaning of a program is unchanged whether it is executed sequentially or in parallel. Furthermore, the results are deterministic; there is no possibility that a parallel program will give one result in one run and a different result in a different run.

In contrast, a concurrent program has concurrency as part of its specification. The program must run concurrent threads, each of which can independently perform input/output. The program may be run on many processors, or on one — that is an implementation choice. The behaviour of the program is, necessarily and by design, non-deterministic. Hence, unlike parallelism, concurrency has a substantial semantic impact.


병렬 프로그래밍은 성능 향상을 목적으로 명시적으로 멀티 프로세서를 사용함을 의미합니다. 반대로 동시성 프로그래밍은 동시성 자체가 명세에 포함되는 개념입니다. 프로그램이 여러 개의 동시적인 쓰레드로 실행이 되고 따라서 결과가 비결정적이다라는 사실까지 말입니다. 병렬 프로그래밍과 마찬가지로 멀티 프로세서를 사용할 수도 있지만 이는 필수적인 것이 아니라 구현 세부사항일 뿐입니다.

여기까지는 지난 번에 보여드렸던 설명과 동일합니다. 위 정의에는 여기에 덧붙여 더 중요한 정의가 나오는데, 바로 시멘틱에 관한 부분입니다. 병렬 프로그래밍은 프로그램의 시멘틱에 전혀 영향을 미치지 않습니다. 병렬 프로그래밍에서는 e1 + e2를 계산할 때 e1과 e2를 각각의 프로세서에서 실행을 하던 하나의 프로세서에서 실행을 하던 겉으로 보이는 결과는 반드시 같아야 합니다.

하지만 동시성 프로그래밍에서는 멀티쓰레드 사용 자체가 시멘틱에 포함되어 있기 때문에 쓰레드가 어떻게 수행되느냐에 따라서 결과가 달리나올 수 있음을 내포하고 있습니다. 즉, 시멘틱이 달라질 수 있다는 사실이 깔려있습니다.

앞으로는 이런 사실을 염두에 두고 동시성 프로그래밍과 병렬 프로그래밍 용어를 명확히 구분해서 사용하려고 합니다.

비고: Tackling the Awkaward Squad는 굉장히 읽을 만한 글입니다. 헤스켈에서 가장 어려운 부분이지만, 쓸만한 프로그램을 만들기 위해서는 반드시 알아야 하는 입출력(IO), 동시성(concurrency), 예외 처리(exception), 외부 함수 호출(foreign-language call)을 다루고 있습니다. 특히, IO monad의 operational semantics를 설명하는 부분에 감동했습니다 +_+

Scala

Posted 2008. 3. 27. 01:50
2008년은 함수형 언어가 우리 개발자들의 품으로 한 걸음 더 다가오는 해가 아닐까 싶습니다. .NET에서는 이미 OCaml의 .NET 버전인 F#을 내놓고 열심히 홍보를 하고 있고 자바 진영에서도 JVM을 타겟으로 하는 함수형 언어인 Scala가 조금씩 홍보를 시작한 것 같습니다. (개발 자체는 2001년에 EPFL에서 이미 시작되었지만요.) 마침 올해 초에 IBM developerWorks에 The busy Java developer's guide to Scala: Functional programming for the object oriented 라는 튜토리얼 형태의 글이 올라오기도 했습니다.

구글에서는 이미 2006년에 Scala를 만든 Martin Odersky를 초청해서 The Scala Experiment: Better Language Support for Component Systems? (PDF)라는 제목으로 Scala에 대한 강연을 하기도 했습니다. 참고로 Martin Odersky는 자바 진영에서 굉장히 유명한 사람인데 자바의 제네릭스를 추가하기 위한 Pizza, GJ 프로젝트에 참여했고 이 프로젝트는 결국 자바 5의 제네릭스가 되었습니다. 스스로가 함수 언어에 대한 이론을 기반으로 자바에 실용적인 활용 방안을 모색해 온 사람이라 Scala에도 이런 철학이 그대로 녹아들어가 있습니다.

Scala의 기본적인 아이디어는 함수형 언어의 특징을 객체지향 언어인 자바에 잘 집어넣자는 것입니다. 대신 Pizza나 GJ 처럼 자바 언어를 어느 정도 개선하는 스타일이 아니라 아주 새로운 언어를 만들되 타겟만 JVM으로 하는 방식을 취했습니다. Scala의 초기 논문을 보면 언어의 출발은 Java의 복잡한 컴포넌트를 쉽게 조합할 수 있는 도구에 있었습니다. 컴포넌트 재사용의 편의는 고차함수나 Algebraic Data Type 등 함수 언어 아이디어를 OO와 접목하는 걸로 해결하려고 했고요.

코드는 대충 아래와 같이 생겼습니다. 정작 타이핑을 하는 언어임에도 불구하고 타입 추론의 큰 도움으로 마치 동적인 스크립트 언어 같은 느낌을 줍니다. 실제로 같은 일을 하는 자바 코드에 비해 1/2, 1/3 정도로 코드량이 주는 것을 확인하실 수 있습니다.


object Timer
{
def periodicCall(seconds: Int, callback: () => Unit): Unit =
{
while (true)
{
callback()
Thread.sleep(seconds * 1000)
}
}

def main(args: Array[String]): Unit =
{
periodicCall(1, () =>
Console.println("Time flies... oh, you get the idea."))
}
}


개인적으로 JVM을 타겟으로 하는 프로젝트를 할 일이 생기면 과감히 Scala를 써볼 생각도 있습니다. Scala에 대한 구체적인 소개는 다음 기회에 자세히 다뤄보려고 합니다. 미리 궁금하신 분은 Scala 홈페이지를 방문해주세요.


파이썬 바인딩

Posted 2008. 3. 27. 01:04
까멜레오 프로젝트에 대해서 자주 듣는 질문 중에 하나는 왜 파이썬을 사용했냐는 것입니다. Django 같은프레임워크를 이용한 웹프로그래밍도 아니고 퇴근을 일찍하기 위한 잡무 스크립트를 작성하는 것도 아닌 데스크톱 어플리케이션에 파이썬을 메인 언어로 사용했다는 이야기를 들으면 많은 분들이 어리둥절해 하시기 때문입니다.

파이썬을 사용해서 얻는 가장 큰 장점은 C 언어 바인딩이 편리하다는 사실입니다. 파이썬을 메인 언어로 선택한 이유는 수 십 개의 오픈소스 프로젝트를 가져다가 접목하는 글루(glue) 언어로 파이썬이 장점을 가진다고 생각했기 때문입니다. 특히 리눅스 GNOME 진영의 프로젝트는 대부분 파이썬 바인딩을 제공하고 있습니다. GNOME 프로젝트 자체도 코어는 C/C++을 이용하고 UI와 스크립팅 이슈는 대게 파이썬으로 처리하는 경향이 강합니다.

특히 Glib 라이브러리를 사용하는 (정확히는 GObject) 프로젝트들은 codegen을 이용해 C 헤더 파일에서 파이썬 바인딩을 자동으로 생성해 낼 수 있습니다. 물론, 필요에 따라 일부 함수들을 오버라이드해야 하긴 하지만 언어 바인딩을 자동으로 생성할 수 있다는 장점은 파이썬을 선택한 가장 큰 이유였습니다. IBM DW에 보시면 Wrap GObjects in Python이라는 글이 C 코드에서 파이썬 바인딩을 만들어내는 방법을 잘 보여주고 있습니다.

또한 파이썬 표준 라이브러리에 포함되어 있는 ctypes도 굉장히 유용하게 사용됩니다. 특히 C로 이미 작성된 공유 라이브러리(DLL)를 불러 쓸 때 C 코드를 전혀 작성할 필요 없이 외부 함수를 불러 쓸 수 있다는 것은 파이썬의 큰 장점입니다. pyglet 같은 프로젝트는 ctypes만을 이용해 운영체제의 윈도 시스템과 OpenGL 바인딩을 모두 구현해놨습니다.

파이썬은 이 외에도 C와 파이썬을 혼용할 수 있게 해주는 수많은 외부 함수 인터페이스가 존재합니다. 하지만 과학적 계산 등의 코드가 거의 없는 까멜레오 프로젝트에서는 이런 식의 조합은 거의 필요가 없었습니다. 파이썬 코드도 사실상 내부적으로 C 코드를 불러 쓰는 경우가 대부분이었기 때문입니다.

이렇게만 쓰면 파이썬을 사용한 것이 굉장히 좋은 선택이었던 것처럼 보이는데, 파이썬을 본격적으로 사용하면서 겪은 시행 착오와 실수도 엄청 많습니다. 이 부분에 대해서는 다음 기회에 따로 한 번 정리해 보려고 합니다.



사용자 삽입 이미지



얼마 전에 과학소설(SF) 계의 큰 별이자 미래학자이기도 한 아서 클라크경(Sir Athur Charles Clarke) 운명하셨다고 합니다. SF 소설을 즐겨보는 편은 아니지만 이 분의 명성은 익히 알고 있었는데 안타까운 소식입니다.

클라크 경은 예측에 대한 세 가지 법칙으로 유명한데요. 다음과 같습니다.







1.뛰어난 하지만 나이가 든 노과학자가 어떤 것이 가능하다고 하면 거의 확실히 그의 말이 옳다. 그가 어떤 것이 불가능하다고 하면 틀렸을 가능성이 매우 높다.
2. 가능성의 극한을 발견하는 유일한 방법은 가능한 것들을 조금만 넘어 불가능한 것으로 가보는 것이다.
3. 충분히 발전한 기술은 마법과 구분할 수 없다.


저는 3번 법칙이 제일 와닿습니다. 제가 자동차, 휴대폰, TV 등을 들고 100년 전으로만 돌아가도 저는 종교 단체 지도자가 되고도 남을 엄청난 기적을 보여줄 수 있을테니깐요.

요즘은 각종 방법론(개발 방법론, 학습 방법론) 등에도 비슷한 생각을 가지게 되었습니다. 한 때 소프트웨어 공학에서 말하는 전통적인 각종 방법론과 프로세스 개선 방법에 대해서도 공부해보고, 나중에는 애자일 방법론과 XP 등에 대한 각종 도서들을 열심히 읽고 느끼는 바도 많았습니다. 하지만 시간이 지날수록 내가 처한 현실은 별로 변하는 것 같지 않은데 방법론은 점점 고도화되고 어려워진다는 느낌을 피할 수가 없더군요.

클라크 경의 3번 법칙을 변형해서 방법론에 적용해보면 충분히 발전한 방법론자는 도인과 구분할 수 없다라는 결론이 나옵니다. 가끔 방법론의 자기 개선(자기 발전적인) 학습에 너무 치우친 나머지 일반 개발자들과 괴리감을 보이고 방법론을 강의하는 건지 인생 철학을 강의하는 건지 심리학을 강의하는 건지 전혀 모르겠습니다. 듣고 보면 좋은 말인 것 같은데 뭘 해야할지 모른다고 해야할까요.


Static Type Inference for Ruby

Posted 2008. 3. 26. 04:29
저는 정적 타입 시스템 옹호자이기 때문에 루비, 파이썬 등의 스크립트 언어의 성공을 항상 못마땅하게 생각하던 사람입니다. 농담이고요. 동적인 스크립트 언어가 성공하면서 동적 타이핑이 많은 연구와 노력이 들어간 정적 타이핑보다 훨씬 좋은 것처럼 생각하는 사람들이 늘어나면서 안타까웠던 것은 사실입니다.

실제로는 루비나 파이썬 같은 동적 스크립트 언어도 정적 타이핑을 통해 여러 가지 이득을 볼 수 있습니다. UMD에서 나온 OOPSLA08에 제출한 Static Type Inference for Ruby도 그런 시도 중의 하나라고 볼 수 있습니다. 여기서 제시한 DRuby는 다음의 특징이 있습니다.


1. GLR(Generalized LR) 파서로 루비 문법에서 disambiguation을 위한 규칙을 분리해 내고 확장하기 쉽게 만들었다.

2. RIL(Ruby Intermediate Language)를 정의해 소스 코드를 쉽게 분석할 수 있도록 하였다.

3. 타입 어노테이션 언어와 타입 추론 시스템을 도입해 루비 프로그램의 타입을 정확히 알 수 있도록 하였다.


참고로 근래에는 정적 타이핑과 동적 타이핑을 적절히 결합하려는 시도가 많이 있습니다. soft typing, dynamic type, quasi-static typing, gradual typing 등으로 용어도 다양하네요.

Parallelism and Concurrency 1

Posted 2008. 3. 26. 02:36
여러분은 Paralleism과 Concurrency를 구분해서 쓰시나요? 저는 그동안 Parallel Programming과 Concurrent Programming을 구분하지 않고 그냥 병렬 프로그래밍이라고 번역해왔는데 Parallelism과 Concurrency의 의미를 구분해서 쓰는 경우도 있더군요. 헤스켈 컴파일러인 GHC 매뉴얼 Cocurrent and Parall Haskell에 보면 두 용어를 다음과 같이 정의해 놨습니다.


Paralleism

헤스켈 프로그램을 성능 향상을 목적으로 여러 프로세서에 돌리는 것을 의미한다. 이 과정은 의미 변화 없이 눈에 보지이 않게(자동으로) 이루어지는 것이 이상적이다.


Concurrency

프로그램을 I/O를 수행하는 여러 개의 쓰레드를 구현하는 것을 의미한다. Concurrent 헤스켈 또한 병렬 머신에서 실행될 수 있으나, Concurrency의 주 목적은 성능 향상이 아니라 이 방법이 프로그램을 작성하고 가장 쉽고 직접적인 방법이기 때문이다. 쓰레드가 I/O를 수행하기 때문에 프로그램의 의미는 비결정적(non-deterministic)이다.

저는 여전히 두 용어를 혼용해서 쓰는 편이고 웹에 있는 상당수의 글들도 마찬가지인 것 같은데 정확한 가이드라인이 있는 것인지 궁금하군요. 더불어 Concurrency는 뭐라고 번역하면 좋을까요? 동시성은 영 어색한 단어 같고, 병렬로 하자니 Parallelism과 구분이 안 되는 것 같고 고민이네요.


하스켈과 병렬 프로그래밍

Posted 2008. 3. 26. 02:25

제가 함수형 언어에 직접적인 관심을 가지게 된 계기는 함수형 언어가 멀티코어 프로그래밍(병렬 프로그래밍)에 대한 가장 활발한 연구 결과를 내어놓고 있기 때문입니다. 부작용(side-effect)이 없고 연산(evaluation) 순서에 제약이 덜하다는 함수 언어 자체의 특징이 병렬화와 잘 어울리는 부분도 있습니다만, 언어 특징과 별개로 병렬 프로그래밍에 대한 최신 생각들이 함수 언어에 가장 먼저 적용되는 부분도 무시할 수 없습니다.

병렬 프로그래밍하면 주로 얼랑(Erlang)이 많이 주목받고 있습니다. 함수 언어 중 거의 유일하게 상업적인 용도로 이용된 적이 있다는 사실 때문에 근래에 관심을 가지게 되신 분이 많이 있는것 같습니다. 하지만 경량 프로세스(lightweight process)와 메시지 패싱 방식은 순차적 프로그래밍에 익숙한 기존 개발자들이 쉽게 적응할 수 있는 모델이 아니라 굉장히 큰 진입 장벽이 존재합니다. 자바 개발자가 파이썬으로 코딩해도 여전히 자바 스타일인 것처럼 락 기반 멀티쓰레드 프로그래밍에 익숙한 개발자가 태스크를 잘게 쪼개고 수많은 경량 프로세스를 통해 메시지를 주고 받는 모델로 개발하기란 쉽지 않습니다.

더불어 얼랑은 함수 언어 연구와 깊은 상관관계를 맺고 있는 정적 타입 시스템의 장점을 전혀 살리지 못하는 동적인 언어라는 사실도 약점입니다. 얼랑이 주로 통신 장비 등 상당한 안정성이 요구되는 분야에 사용된 언어임에도 불구하고, 버그를 일찍 쉽게 발견할 수 있게 도와주는 정적 타입 시스템의 도움을 전혀 못 받고 있다는 사실을 조금 의외입니다. 런타임에 쉽게 오류에서 회복되는 것도 중요하지만 애당초 버그를 줄일 수 있었다는 더 좋았을 텐데 말이죠.

하스켈은 병렬 프로그래밍과 관련해서는 가장 많은 옵션을 제공하는 언어입니다. 일단 가장 유명한 세 가지는 다음과 같습니다.



1) Concurrent Haskell

forkIO 등의 함수를 이용해 쓰레드를 생성하고 죽일 수 있으며 MVar 등 공유 메모리를 이용해 동기화하는 모델입니다.

2) Parallel Haskell

Paralle Haskell은 `par`와 `seq` 등을 이용해 순차적인 프로그램에 병렬화할 수 있는 단서를 달아주는 방법입니다. 어떤 부분이 동시에 수행될 수 있는지만 알려주면 하스켈이 알아서 쓰레드를 생성하고 동기화를 해줍니다. 아래 글을 참고하세요.

3) Software Transaction Memory

1) 번 모델을 발전시킨 것으로 데이터베이스의 트랜잭션처럼 특정 코드를 모 아니면 도(all or nothing)로 실행시켜주는 방법입니다. 직접 메모리에 쓰는 대신에 트랜잭션 로그를 만들고 한 번에 커밋하는 방식입니다. 도중에 누가 방해하면 처음부터 다시 실행하면 됩니다. retry를 이용해 컨디션 변수를 대신해 블로킹을 할 수도 있습니다.

락 기반 멀티쓰레드 프로그래밍의 문제점인 데드락, 레이스컨디션 등이 불가능하고, 규모 가변성(scalability)이 뛰어난 편이라 병렬 프로그래밍 계의 총아로 떠오르고 있는 분야입니다.


멀티 코어 프로그래밍과 헤스켈

Posted 2008. 3. 26. 02:01
멀티코어 시대가 오면서 병렬 프로그래밍이 성능 향상의 중요 방법으로 떠오르고 있습니다. 하지만 실제 개발 환경에서 개발자가 멀티 코어를 효율적으로 활용하기는 쉽지 않습니다.

다음은 0에서 40까지 피보나치 수열을 계산하는 간단한 C 프로그램입니다.

long long fib(long long n) {
  if (n < 2) {
    return 1;
  }
  return fib(n - 2) + fib(n - 1);
}

int main(int argc, char ** argv) {
  long long n = 0;

  for (n = 0; n <= 40; n++) {
      printf("fib(%lld) = %lld\n", n, fib(n));
  }

  return 0;
}

좀 더 효율적으로 짤 수도 있겠지만 계산량이 많은 프로그램의 예를 들기 위해 의도적으로 비효율적인 방식으로 작성했습니다. 이 프로그램을 GCC에서 최적화 옵션을 최고로 주고 컴파일한 후 실행하면 제 컴퓨터(1.66GHZ 코어 2 듀오 1GB 램)에서 대략 12.94초가 걸렸습니다.

gcc -O3 fib.c -o fib.exe
$ time ./fib.exe
real    0m12.940s
user    0m12.921s
sys     0m0.015s


같은 프로그램을 헤스켈로 작성하면 다음과 같습니다.

import Control.Monad
import Text.Printf

fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

main = forM_ [0..40] $ \i ->
        printf "fib(%d) = %d\n" i (fib i)


ghc -O2 fib.hs -o fib.exe --make
$ time ./fib.exe
real    0m12.454s
user    0m0.031s
sys     0m0.015s


위 프로그램을 컴파일해서 실행하면 12.45초가 걸립니다. 약간이긴 하지만 오히려 C 보다 빠릅니다. 성능을 논하기에는 너무 간단한 예이긴 하지만 함수형 언어가 느리다고 생각하는 것과는 상반된 결과입니다.


위 두 예제는 멀티코어를 전혀 활용하지 못하는 프로그램입니다. fib.exe 실행시켜 놓고 윈도 작업 관리자로 확인해보면 CPU 점유율이 50%에 머물고 있는 것을 볼 수 있습니다. 두 개의 코어 중에서 하나만 죽어라고 쓰고, 나머지 하나는 놀고 있기 때문입니다. 이런 식이면 1.66GHZ 짜리 코어를 10개를 갖다 붙여도 위 프로그램은 성능은 전혀 개선되지 않게 되겠죠.

소프트웨어 성격에 따라 차이는 있겠지만 과거와 달리 멀티 코어 시대에는 소프트웨어 성능 향상을 위해 개발자의 명시적인 개입이 필요하다는 이야기가 여기서 나옵니다.

C로 작성된 프로그램을 멀티 코어를 이용하도록 개선하기 위해서는 pthread 같은 쓰레드 라이브러리를 이용해 명시적으로 쓰레드를 만들고 일을 쪼개서 데이터를 처리하고 공유 메모리를 통해 동기화를 해야할 것입니다. 간단한 예제이지만 제대로 짜기가 무척 어려우므로 실제로 코드를 작성하지 않고 넘어가겠습니다.


대신에 헤스켈은 순차적으로 작성된 프로그램을 병렬화하는 것이 상대적으로 매우 쉽습니다. fib2.hs는 fib.hs에 병렬화(parallelism)을 위한 어노테이션(`par`와 `seq`)을 덧붙인 코드입니다.

import Control.Parallel
import Control.Monad
import Text.Printf

cutoff = 30

fib' :: Int -> Integer
fib' 0 = 0
fib' 1 = 1
fib' n = fib' (n-1) + fib' (n-2)

fib :: Int -> Integer
fib n | n < cutoff = fib' n
      | otherwise  = r `par` (l `pseq` l + r)
 where
    l = fib (n-1)
    r = fib (n-2)

main = forM_ [0..40] $ \i ->
            printf "fib(%d) = %d\n" i (fib i)


헤스켈이 병렬 프로그래밍을 하는 방법에는 Control.Concurrent 모듈을 이용해 명시적으로 쓰레드를 생성하고 MVar나 STM을 이용해 공유 메모리를 제어하는 방법도 있지만, 위 예제처럼 Control.Parallel 모듈의 par와 seq 함수를 이용하는 방법도 있습니다.

위 예제는 특정 cutoff보다 작은 숫자는 쓰레드를 생성하지 않고 직접 계산을 하되, cutoff보다 크면 쓰레드를 만들라는 힌트를 줍니다. a `par` b는 a를 계산(evaluation)할 때 CPU가 놀고 있으면 쓰레드를 만들어서 작업하라고 런타임에 힌트를 줍니다. a `seq` b는 b을 계산하기 전에 a를 먼저 계산(순차적)하라는 뜻으로 이해하시면 됩니다. 따라서 위 예제는 fib (n - 2)는 별도의 쓰레드로 계산하고 동시에 부모 쓰레드에서 fib (n - 1)을 계산해 두 결과를 합쳐서 리턴하게 됩니다.

GHC에 -threaded 옵션만 주면 자동으로 멀티 코어를 활용하는 런타임이 생성됩니다. 실행할 때는 명시적으로 +RTS -N2로 코어가 2개임을 지정해주면 됩니다.

ghc -O2 fib2.hs -o fib2.exe --make -threaded
time ./fib2.exe +RTS -N2
real    0m10.171s
user    0m0.015s
sys     0m0.031s


위 프로그램은 명시적으로 쓰레드를 생성하지 않았고 락을 잡지도 않았지만 멀티 코어를 활용하게 됩니다. 수행 결과 싱글 코어일 때보다 조금 더 빠른 10.17초가 걸렸습니다. 프로그램이 작다보니 쓰레드를 생성하는데 오버헤드가 있어서 성능이 2배가 되지는 않는 걸로 보입니다. 대신에 작업 관리자를 보시면 이번에는 CPU를 거의 100% 쓰고 있다는 사실을 확인하실 수 있습니다.

제 컴퓨터에서는 테스트해볼 수 없었지만 위 프로그램은 코어 수가 2배 4배가 되면 그에 따라 성능이 증가하게 됩니다. 즉, 멀티 코어가 대세가 되면 코어를 제대로 활용하지 못하는 C 프로그램보다 여러 코어를 잘 활용할 수 있는 함수 언어가 더 빠른 세상도 올 수 있는 것입니다.


참고: 헤스켈 컴파일러인 GHC는 공유 메모리 멀티프로세서만 지원합니다. 클러스터나 싱글 멀티프로세서 등은 GHC에서 파생된 GPH(Glasgow Parallel Haskell) 프로젝트를 사용하시면 됩니다.

Monad tutorials timeline

Posted 2008. 3. 19. 02:34

4월 마소 원고 주제를 하스켈을 이용한 STM(Software Transaction Memory)으로 잡고 마무리를 하고 있습니다.

지금은 모나드(monad)에 대한 설명을 추가하려고 모나드 튜토리얼을 좀 찾아보고 있었습니다. 모나드는 추상적인 개념이다보니 하스켈 입문자들이 가장 어려워 하는 부분이기도 하고 깊이 있게 이해하기가 쉽지 않은 부분입니다. 과거에는 찾아볼 자료도 별로 없었는데 최근에 하스켈 개발자가 늘고 있어서 그런지 2006, 2007년도에 모나드 관련 글이 굉장히 많이 나왔더군요. Monad tutorials timeline에 보시면 모나드 튜토리얼이 시대별로 어떻게 변천해 왔는지도 보실 수 있습니다.

튜토리얼에는(혹은 튜토리얼이기 때문에) 보통 모나드의 이론적인 배경인 카테고리 이론(category theory)과의 연관 관계를 설명하는 부분이 빠져 있어서 살짝 아쉽네요.

libmms win32 branch

Posted 2008. 3. 18. 12:38
예전에 libmms 포팅했다는 글을 올렸었는데, 이 패치가 적용된 branch가 libmms 프로젝트 페이지에 올라와 있습니다. 저는 main branch에 통합될 것을 감안하고 Glib 기반으로 운영체제간 호환성 부분을 해결했는데, 별도 branch가 되면서 별로 큰 의미가 없는 것 같기도 하네요. GNOME 쪽 프로젝트가 윈도로 포팅된 경우가 아닌 이상 DirectShow 대신에 굳이 libmms를 직접 사용할 이유가 없기도 하니 별 상관은 없겠죠.



A taste of Haskell

Posted 2008. 3. 18. 03:17
Simon Peyton-Jones가 2007년 OSCON에서 발표한 A taste of Haskell 발표를 봤습니다. 함수형 언어 Haskell을 소개하는 자리인데 1편, 2편 각각 90분씩 총 3시간의 분량으로 비함수형 프로그래머들에게 함수형 언어의 장점과 특징을 소개하고 있습니다. 슬라이드도 여기에서 보실 수 있습니다.

xmonad라는 X 윈도용 윈도 매니저 프로그램을 이용해 함수형 언어에서 간과하기 쉬운 IO 사용이나 외부 함수(foreign function) 호출 등도 다루고 있습니다.

사용자 삽입 이미지

참고로 SPJ 이 분은 GHC(Glasgow Haskell Compiler)를 비롯해 함수형 언어 이론 및 구현에 많은 업적을 남기신 분입니다. 함수형 언어의 실무와 이론을 모두 해본 사람으로 함수형 언어에 대해 누구보다도 열정이 강하고 할 말이 많은 사람이기도 합니다.

이쪽 분야에 관심을 가지면서 보니 참 똑똑한 사람들이 많더군요 ㅠ.ㅠ







뱀다리: OSCON 하니깐 생각나는데 까멜레오 프로젝트도 올해 3월 OSCON에서 발표를 신청했었습니다. 영어로 열심히 설명을 적었는데, 아직 오픈소스를 못하고 있어서 참가 여부가 매우 불투명한 상태라서 문제이지만요.

회사일에 치여 PL 공부 안 한지가 너무 오래되서 요즘은 환기 차원에서 브라운 대학의 Shriram Krishnamurthi 교수가 쓴 Programming Languages: Application and Interpretation를 지하철 오가면서 틈틈이 보고 있습니다. 이 책은 대학에서 프로그래밍 언어 과목을 가르치면서 만든 프로그래밍 언어(PL) 교과서입니다. Pierce의 Types and Programming Languages처럼 무겁과 이론적인  설명을 피해 비교적 가벼운 접근 방법을 취하고 있습니다.


이 책에서 언급하지만 프로그래밍 언어는 크게 4가지로 나눠서 공부할 수 있습니다.

1. 문법(syntax)
2. 의미(semantics)
3. 라이브러리(library)
4. 용법(idioms)


1번 문법과 관련해서는 오토마타 형식 언어와 컴파일러의 파서 쪽에서 주로 다루고 있습니다. 3번과 4번은 각 언어 커뮤니티에서 방대한 정보를 제공하고 있고요. PL 이론서는 보통 1, 3, 4번은 과감히 생략하고 언어의 의미를 어떻게 정의할 것인가에 초점을 맞추고 있습니다. 이 책도 역시 프로그래밍 언어의 의미를 부여하고 검증(sound, complete)하는 방법에 대해 이야기합니다.

PL 이론서들은 주로 Operational Semantics, Denotational Semantics, Axiomatic Semantics 등으로 접근 방법을 달리하고 있습니다. 그나마 이해하기 쉬운 게 Opeational Sematnics인데, 입문자는 이 역시 이해하기가 어렵습니다. 이 책은 PL 입문자들을 위해 Operational Semantics에 접근하기 위해 먼저 간단한 인터프리터를 구현해 나가는 방법을 택하고 있습니다. 또한, Continuation Passing Style을 설명하면서 웹프로그래밍을 예로 드는 등 실제 프로그래밍과 연결 고리를 찾기 위해서 고심한 흔적도 보입니다.

너무 머리 아프지 않은 PL 입문서를 찾으신다면 권장해 드립니다. 물론 그런 후에도 Pierce 책은 필독입니다.

libmms 포팅

Posted 2008. 2. 27. 00:55
리눅스 쪽에서 mms/mmsh 스트리밍 소스를 사용하기 위한 libmms라는 프로젝트가 있습니다. 현재 0.4까지 올라와있습니다.

저는 오늘 GStreamer Plug-ins Bad에 있는 libmms 플러그인을 사용하기 위해 libmms를 윈도로 포팅하는 작업을 했습니다. 윈도 GStreamer에서 mms 소스를 간단히 테스트하려고 시작했는데, libmms가 제대로 포팅이 안 되어 있어서 조금 고쳐서 해본다는 게 결국 꽤 많은 코드를 수정하게 되었군요. 역시나 버리기 아까워서 패치로 묶어서 메인테이너한테 보냈는데 어찌될지는 모르겠습니다.

libmms의 외부 의존성은 소켓 라이브러리 밖에 없긴 한데, 윈도 포트를 고려를 안 해놔서 윈도의 경우 WinSock 함수를 쓰도록 패치를 했습니다. 윈도 프로그래밍 경험이 많지 않아서 역사적인 이유는 모르겠지만, WinSock이 유닉스 계열 소켓 함수와 거의 비슷하면서 조금씩 다른 부분들이 있어서 귀찮더군요.

처음에 WSAStartup을 안 부르고 테스트하다가 한참을 헤맸습니다. 더불어 snprintf나 strcasecmp, strncasemp도 윈도 쪽에 같은 이름으로 함수가 없어서 귀찮더군요. Glib이라도 쓰면 g_만 붙여서 g_snprintf, g_strcasecmp, g_strncasecmp로 사용하면 좋긴 한데, libmms 경우는 Glib 의존성을 제거한 흔적(glib.h)이 남아 있어서 그것도 안 되는 것 같고요.

새삼스러운 질문이지만 C에서 네트워크 관련 라이브러리를 리눅스 혹은 윈도로 포팅하는 가장 쉬운 방법은 무엇일까요?
« PREV : 1 : 2 : 3 : 4 : 5 : 6 : ··· : 13 : NEXT »