GC처럼 동시성 문제를?

Jooyung Han (한주영)
4 min readDec 17, 2017

--

Jafar Husain 씨 트윗에 Haxl이 언급되었네요. 트윗은 Simon Marlow의 최근 발표 동영상을 공유하는 것이고요.

JS만 얘기하던 Jafar Husain이 어쩐 일로 Haxl 발표를 언급했을까?

Haxl은 몇 년 전 발표와 페이퍼를 읽고 관심을 가지게 되었습니다. 최근에는 별다른 소식이 없었는데 이번 Strange Loop 발표는 무슨 내용일까 싶어서 동영상을 보게 되었습니다.

Haxl은 원격 데이터 펫칭에 발생하는 동시성 문제를 해결하도록 도와주는 미들웨어 성격의 라이브러리입니다. 예를 들면 아래처럼 별 생각없이 작성할 수 있는 간단한 코드에서 friendsOf 라는 원격 데이터 펫칭 두 개가 동시에 배치 처리될 수 있도록 도와줍니다.

-- Haskell
numCommonFriends u1 u2 = do
l1 <- friendsOf u1
l2 <- friendsOf u2
return $ length $ intersect l1 l2

이번 발표는 Haxl과 관련해서 새로울 게 없었습니다. 그런데 새로운 관점을 제공하네요. 바로 Garbage Collection이 개발자들에게서 메모리 관리라는 큰 부담을 덜어준 것처럼 동시성 관련한 문제도 자동화할 수 있지 않겠느냐는 것이었습니다. 놀랍죠?

최근 많은 언어들이 지원하는 Async 코루틴을 이용하면 비동기 코드를 조금 더 편하게 작성할 수 있다고 하지만 여전히 개발자가 신경써야 할 것들이 있습니다.

// JS
async function numCommonFriends(u1, u2) {
const l1 = await friendsOf(u1);
const l2 = await friendsOf(u2);
return intersect(l1, l2).length;
}

위 코드는 friendsOf 두 개가 순차적으로 요청을 처리하기 때문에 동시성 관점에서 보자면 비효율적입니다.

// JS
async function numCommonFriends(u1, u2) {
const p1 = friendsOf(u1);
const p2 = friendsOf(u2);
return intersect(await p1, await p2).length;
}

이제 friendsOf 쿼리 두 개가 동시에 처리됩니다. await라는 suspension point 가위치가 바뀌었습니다. 사소해 보이는 이 차이로 인해 동작이 달라집니다. 바로 이 부분이 Haxl에서 자동화하려는 포인트입니다. 이런 미세 조정을 신경쓰지 않아도 된다면? 이란 것이죠.

위 예제 함수만 보자면 이 정도 신경쓰는 것이 무슨 대수냐 싶습니다. 하지만 이 함수가 다른 async 함수들에서 사용되고, 그 함수들은 또 다른 async 함수들을 호출하고 … 그런 상황에서 효율적인 병렬화를 직접 고려한다는 것은 쉬운 문제가 아닙니다. 단순한 상황에서는 GC 도움 없이 직접 메모리 관리하는 것이 어렵지 않지만 실제는 그렇지 않은 것과 마찬가지입니다.

현재 Haxl은 여러 언어/플랫폼에 포팅되었지만 실제로 위와 같은, 코드 작성자가 직접 동시성 이슈를 고려하지 않아도 되는, 정도의 이득을 다른 라이브러리들은 제공하지 못합니다. 그 이유는 Haxl을 통한 동시성 자동화(?)의 절반을 하스켈 컴파일러가 담당해주기 때문입니다. 포팅된 라이브러리만으로는 그 반쪽의 이득을 제공할 수 없습니다.

예를 들어, 위 예제 코드에서 friendsOf u1의 결과 l1friendsOf u2에서 사용되지 않음을 확인하고 컴파일러는 순차적 호출 코드를 아래처럼 동시 실행 코드로 바꿔줍니다.

numCommonFriends u1 u2 = do
(l1, l2) <- (,) <$> friendsOf u1 <*> friendsOf u2
return $ length $ intersect l1 l2

Jafar Husain이 트윗에 언급한 내용을 보면 PureScript로 Haxl이 포팅되었기 때문에 이제 Node 환경에서도 사용할 수 있을 것이라고 하네요. 찾아보니 라이브러리 파트는 Fetch라는 이름으로 포팅되었고요. 컴파일러 확장 부분 역시 같은 개발자가 구현해 넣었군요. 하지만 확장 내용을 살펴보면 새로운 키워드를 추가한 것이기 때문에 Haskell의 그것과는 사용성 측면에서 조금 부족하지 않을까 추측해 봅니다. (Haskell은 기존의 do 문법을 Applicative로 변환해주는 확장이지만, PureScript는 ado라는 새로운 do 문법을 추가하여 적용 범위를 제한했습니다. 그러면 결국 사용자가 상황에 따라 do/ado를 선택해야 하므로 온전히 부담을 덜어주진 못합니다.)

그나마 PureScript는 Haskell을 닮은 언어이기 때문에 이 정도까지 비슷하게는 구현해낼 수 있지만 다른 언어/환경에서는 컴파일러에 의한 자동 변환 부분이 해결되지 않을 것 같은데, 그건 또 모르죠. 또 어떤 괴물이 나타나 이런 문제 다 해결할지. :-)

실제로 GC만큼이나 발전하려면 아직도 가야할 길이 많이 남았겠지만 앞으로도 관심을 가지고 지켜보려 합니다.

--

--

Jooyung Han (한주영)

가끔 함수형 프로그래밍 관련 글을 쓰거나 번역합니다. “개미 수열을 푸는 10가지 방법"이란 책을 썼습니다. https://leanpub.com/programming-look-and-say