Unpacking에 관한 추측(Draft)

Unpacking에 관한 추측(Draft)

기본적으로 unpacking은 고려해야 할 방법이다.

이번 simulation에서 만들고 있는 커다란 자료 구조는 크게 두 개로 분리하고 있다.

자주 갱신하는 parameter value와 전혀, 혹은 거의 갱신하지 않는 config value이다.
이 두 가지 value를, 하나의 data 안에 기술하고 있다.

이 두 가지를 어떻게 다루는가에 따라서 memory 사용 효율과 처리 속도가 바뀔 것이다.

그럼 다시 Boxed와 Unboxed가 무엇인가를 살펴보자.

Boxed data

Boxed data는 기본적으로, 그 data의 상태를 기술하는 word와 그 data의 value를 기술하는 word들로 구성된다.
data의 상태를 기술하는 부분은 word 하나로 구성되어있다고 배웠지만, 진짜로 그런지는 잘 모르겠다.
어쨌거나, 최소한 word 하나는 사용한다고 생각하자.

value를 기술하는 부분은 당연히 그 value의 크기에 따른다.
Int를 정의할 때는 word 하나를 사용한다. 문자열이라면 상당한 크기일 것이고, ByteString이라면, 두세 개의 offset 정보와 payload의 배열만큼을 필요로 한다.

Pointer to Boxed data

하나의 boxed data가 어떻게 기술되는지는 알아보았다.
그렇다면 다음는으로 일반적으로 구조체(또는 record, object)라고 불리는 data안에서 boxed data를 담는지 알아보자.

먼저 고려해야 할 점은, value의 크기가 가변적이라는 것이다.
게다가, lazy evaluation의 특징 중 하나인, chunked data, 즉 아직 만들어지지 않은 data의 value는 담을 수도 없다.
즉, 기본적으로 구조체 안에는 value가 담기는 것이 아니라, 독립된 boxed data에의 pointer만이 담겨 있다.

pointer는 일반적으로·당연하게 word 하나이다.

{-# UNPACK #-}

그러면, unboxed data는 무엇일까?
단순하게 말해서, 어떤 data의 상태를 생략한 value만을 지칭하는 것이다.

즉 원래 어떤 구조체 안에 있는 Int field는 Int 값을 직접 담지 않고, Boxed Int data에의 pointer, 그리고 Int data의 상태, 마지막으로 Int value, 즉 word 3 개가 소모되고 있다.
그것을 {-# UNPACK #-}을 이용해서 구조체 안에 Int value를 직접 담으면, word 하나만 소모하면 되니까 word 두 개를 절약할 수 있다.
게다가, pointer를 거치지 않고, Int value의 상태를 참조하지 않아도 되니까 memory access 또한 3번에서 1번으로 줄어든다.
상당한 절약이라고 하지 않을 수 없다.

Is unboxed data is silver bullet?

당연히 모든 경우에 unpacking하면 좋을 리가 없다.

Unpacked된 경우에는, chunk data의 pointer를 대신 넣어둘 수 없으니까 그 값을 미리 계산해야 한다. 즉, 계산을 미루어둘 수 없다.
이건 간단한 문제이다. 필요에 맞추어서 적절히 사용하자.

더 고려해 볼 필요한 문제는 갱신 문제이다.
Unboxed된 이상, unboxed data는 독립적으로 존재하는 data가 아니라, 그것을 담는 다른 data에 종속된 value가 된다.

Haskell이 갖는 중요한 특징 중의 하나가 참조 투명성이다.
그리고 참조 투명성이 가져다주는 이점 중 하나는 기존의 data를 재사용하기 쉽다는 것이다.
실제로 적극적으로 재사용한다.

다음과 같은 data X를 정의해보자.

어떤 Xtype인 data, x = X 1 "Something X"가 이미 존재하고, 이 xxInt의 값을 2로 바꾼 다른 data y를 정의한다고 하자.
let y = x { xInt = 2}와 같은 방식으로 y를 정의하는 경우, 새로 사용하는 memory는, y의 상태를 기술하는 word 하나, yxInt를 가르키는 pointer가 word 하나, Int 2를 담는 boxed data가 word 둘, 마지막으로 기존의 x가 가지고 있던 boxed data인 String xStr을 가르키는 pointer가 word 하나로 총 word 5 개만이 필요하다. x를 정의할 때 word를 5+문자열의 크기만큼 사용했던 것에 비하면 반도 안 된다.

이것은 "Something X"라는 문자열 data가 이 프로그램이 종료될 때까지 절대로 변하지 않을 거라고 보장되어있으니까, xy도 공유하는 것에 대해 어떠한 걱정도 할 필요가 없기 때문이다.

그러면, field xIntxStr을 각각 unpack했을 때 어떤 일이 발생할까?

Case 1: unpack xInt

좋은 일밖에 없다.
어차피 pointer도 word 하나, Int의 value도 word 하나를 사용하니까 unpack해서 value를 직접 담는 게 났다.

Case 2: unpack xStr

문제는 xStr이다.

memory 사용량과 access 횟수, 그리고 data 생성시간, 이 두 가지 면에서 살펴보자.

memory 사용량

Strinrg, 정확히 말해서 Haskell의 리스트 구현 방식을 잘 모르기 때문에 정확한 사용량은 모른다.
일단, 일반적인 List의 정의방식을 따라서 data List a = Cons a | Nil이라고 생각하자.
String :: [Char]이기 때문에 문자열의 길이가 n인 경우, memory 사용량은 (1+1+1)*n + 1정도가 될 것이다.

xStr이 unpack되는 경우, xy가 각각 문자열의 길이에 따라, (3n+1)*2정도 사용하게 될 것이다.
그에 비해서, boxed data인 상태로 내버려둔다면 (3n+1)+2만 사용하면 된다.

쉬운 이해를 위해서 String을 이용했지만, 엄밀하게 말해서 String, 정확히는 리스트는 unpack할 수 없다.
GHC 8.0.x 까지는 > 이외에도 Bool이나 Maybe a, Enum class의 data 등, 복수의 data constructor를 가지는 Sum-type data는 아직 unpack할 수 없기 때문이다.
Unpack은 GHC가 자체적으로 지원하는 부분이고, Haskell 언어상에서 정의된 부분이 아니기 때문에 언어적인 문제는 아니다.
설명에 따르면, 아직 구현이 되지 않았을 뿐이라고 한다.
다행히도 GHC 8.2.1부터 Unpacked sum types이라는 이름으로 unpacking할 수 있게 된다고 한다.
다만, Unpacked sum types는 프로그래머가 필요하다고 지시할 때에만 적용되므로, 여전히 String은 unpack할 수 없을 것이다.

access 횟수

String자체에 대한 access 횟수는 3번에서 1번으로 줄어들 것이다. 이건 Int의 경우와 다르지 않다.

data 생성시간

이게 중요하다.
y를 생성할 때, memory상에 xStr에 정의된 문자열을 완전히 복사할 필요가 있다.
"Something X"라는 문자열은 독립되어있지 않고 xy에 각각 종속되어있기 때문이다.

단순히 xInt의 값만 갱신할 생각이었는데, 커다란 문자열까지 복사해야 한다.
xStr이 빈번하게 갱신되는지 아닌지는 중요하지가 않다. 다른 field가 갱신되면 xStr또한 새로 만들어지는 값에 복사해야 한다.

reconsideration

다시 원래 구상하고 있던 data를 살펴보자.

처음에 unpacking을 알게 된 다음, 매우 빈번하게 참조하지만 거의 갱신되지 않는 config data에 적용해보려고 했다.
빈번하게 참조되는 만큼, unpacking하면 참조하는 비용도 절약될 것이라고.
config data를 SConfig안에 담아두고서, 그 SConfigSomething에 담는 config field도 unpacking 하려고 했는데, memory 사용량과 data 생성시간을 생각해보니까 config는 unpacking하면 안된다는 결론을 얻을 수 있었다.
또한, parameter value라고 하더라도 한번에 생성해서 다른 값들과 공유해서 사용할 가능성이 큰 value 또한 unpacking하면 안된다는 결론을 얻을 수 있었다.

!는 strict / non strict를 다루는 문제이니까, 다른 글에서 다루겠다.

Post Revisions:

CC BY-NC-ND 4.0 This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다

This site uses Akismet to reduce spam. Learn how your comment data is processed.