Framework/Another Lore2009. 7. 5. 20:31
앞서 소개한적이 있지만 

"전산학의 모든 문제는 또 다른 수준의 간접층으로 해결할 수 있다" 
라고 말한 휠러는 

"그러나 그러면 또 다른 문제가 생기는 것이 일반적이다" 라고 덧붙인 적이 있다. 

"또다른 문제"의 대표적인 예로는 공간과 시간적 부담과 코드의 가독성 등을 들 수 있다. 

이 포스팅에서는 이중에서 시간적 부담 즉 속도에 관해서이다. 


일반적으로 대부분의 프레임워크는 하나의 간접층(Layer)이며 위의 명제를 피할 수는 없다. 그러나 프레임워크를 쓴다고 항상 느려진다고 할 수 없는 이유가 있다. 

만약 A -> B 과정이 1초가 걸렸다면
A -> A' -> B 과정은 1초보다 작게 걸린다는 것은 논리적으로 불가능하다. 

그러나 시간은 연속성의 특징을 가지고 있다. 무슨말이냐면 프로그램에서의 시간 혹은 속도의 개념은 하나의 event개념이 아니다. 프로그램을 작성하면 무수히 많은 A -> B 과정을 작성하게 된다. 

이를테면 A가 Client Call이라고 하고 B를 Engine라고 가정했을때 (Engine에는 DB나 NS등의 여러가지를 대입할 수 있다.)

1. Client Call(Engine Library) -> Engine과
2. Client Call(Framework Call) -> Framework(Inner Use Engine Library) -> Engine 라는 경우를 상상해 보자. 

하나의 원자 동작을 보았을때 1은 2보다 항상 속도가 빠르다. 그러나 실제 프로그래밍에서는 2가 종종 더 빠른 이유는(잘 만들었을 경우를 가정했을때...) 연속성의 이유때문이다. 함수 하나 혹은 command 하나는 말 그대로의 원자 event로 이루어지지 않는다. 

그리고 이것때문에 이를테면 1의 동작에 0.1초가 걸린다고 하고 10번 반복한다고 했을때 1은 1초가 걸리지만 2는 1초 이하로 떨어뜨릴수 있다. 

좀더 IT적인 용어로 말하자면 Context Switching 비용 때문이다. 

예를 들어
Row row = Engine.executeCommand() ;
if(row.exist()) Engine.updateCommand() ;
else Engine.insertCommand();

와 같은 코드의 경우 Engine의 row의 exist 여부를 체크하여 2번의 Engine Call을 실행한다. 이 과정에서의 속도는 (0.1 + a(context switching cost)) * 2 + b(Caller cost) 가 소모하게 된다. 

만약 위의 과정을 
Framework.mergeCommand() 라고 바꿀수 있다면 0.1 * 2 + a(context switching cost) + b'(Maybe Lower Caller Cost) + c (framework inner handling cost) 로 수식이 바뀐다. 만약 여기서 a + b> b' + c라면 프레임워크를 사용함으로서 속도가 향상되었다고 말할 수 있다. 추가적으로 좀더 간결하게 프로그램을 작성할 수 있다. 여기서 간결함이란 문제는 이전글을 참조하자. 

3번의 동작이 연속으로 이루어진다면 2a + b> b' + c 정도만 해도 향상될 수 있다. 


두번째 속도가 빨라질 수 있는 이유로 
앞서 원자 event라고 가정했던 1의 과정조차 실제로는 바깥에서는 보이지 않는 내부적으로는 연계된 다른 과정을 거치게 된다.

앞서 원자 action event라고 했던 1이 사실은 
Client Call -> Inner Action 1 -> Inner Action 2 -> Engine Inner으로 이루어져 있다가 가정하자. 
각각의 비용은 a + b + c + d  라고 가정하자. 

만약 
Client Call -> Inner Action 1 -> NewFramework -> Inner Action 2 -> Engine Inner로 작성하였다면..
a + b + @(framework cost) + c + d 의 비용이기 때문에 역시 단순 논리로 봤을때는 빨라지지 않는다. 

그러나 프로그래밍이 다중 사용자 환경(One User 프로그램의 경우에도 Multi Thread로 작성된다면 마찬가지이다. )이고 Action 2가 공유될수 있는 Action이라고 한다면 얘기가 달라진다

Inner Action2을 사용자 혹은 여러 Thread가 공유할 수 있다면

N번의 Call이 발생했을때. 
N(a + b + c + d)의 비용이 N(a + b + @ + d) + c의 비용 수식으로 바꿀수 있다. 
이는 c의 cosr가 total cost에서 얼마의 비중인가에 따라 그 가치가 달라진다. 잘 알려진 예제로 Connection Pooling 같은 과정을 생각한다면 이해하기 쉽다. 

# 컨텍스트 스위치 context switch
멀티테스킹 OS는 서로 다른 처리를 수행하는 처리 단위로 프로세스/쓰레드를 짧은 시간에 전환함으로써 병렬처리를 실현하고 있다. 이때의 프로세스/쓰레드 전환처리를 컨텍스트 스위치라고 한다. 이렇게 컨텍스트 스위치할때 멀티쓰레드는 메모리 공간을 공유하기 때문에 메모리 공간의 전환 처리는 생략할 수 있다. 

메모리 공간을 전환하지 않게 되면 CPU 상의 메모리 캐시(정확히는 TLB Translation Lookaside Buffer : 메모리의 가상 주소를 물리 주소로 변환하는 처리를 고속화하기 위한 캐시로 CPU 내부의 구조다. 컨텍스트 스위치가 일어나면 TLB가 초기화되는데 이 영향에 의한 TBL 캐시 미스는 상대적으로 비용이 많이 든다.)를 그대로 유지할 수 있는 등의 큰 이점이 있으므로 멀티 프로세스에 비해 성능에 미치는 영향은 현저하다. 

이러한 이유로 위의 경우 자바와 예로 든 엔진(예컨데 DB)의 변환은 멀티 프로세스 관계일때 더 차이가 두드러진다. 





원래 주제와 좀 떨어지지만 휠러 얘기가 나왔으니 "발생하는 또다른 문제"중 가독성의 얘기를 해보자. 

여기서의 가독성은 동전의 양면과 같다. 사실 맨 처음 웹 프로그래밍을 한다고 했을때 스프링을 사용하는 것은 그다지 좋지 못하다.(라고 생각한다.) 스프링의 스펙은 그리 만만치 않다. 오히려 기본적인 서블릿 프로그래밍보다 더 많은 걸 알아야 한다. 스프링 프레임아웤의 유용성을 떠나 아이러니한것은 중급개발자의 반복작업을 줄여주기 위한 프레임워크가 오히려 초보개발자용 프레임워크로 둔갑한다는 것이다. 

어쨌거나 휠러가 지적한 것은 프레임워크의 겉핧기 사용등의 프레임워크의 내부에 대한 이해 없이 접근하면 작성하는 프로그래머도 또 그걸 읽는 프로그래머(그 사람이 해당 프레임워크에 대해 알고 있는지와 상관없이)도 고통이 된다는 사실을 말한 것이다. 

앞서 동전의 양면이라고 한것은 프레임워크에 대한 많은 학습없이 직관적으로 사용이 가능하고 기대대로 프레임워크가 작동한다면 오히려 가독성에 도움이 될 수 있다는 의미이다. 

그런면에서 프레임워크의 직관적 사용은 좋은 프레임워크를 결정하는데 핵심이 된다. 스프링 프레임워크가 직관적이지 못한것은 어찌보면 프레임워크가 커버하는 범위와 관계가 있을지 모른다. 스프링 프레임워크는 너무 많은 일을 하는 프레임워크이고 또 유연성을 위해 XML 형식의 configuration이 지나치게 많고 복잡해서 휠러의 또다른 문제를 벗어나지 못했다. 











'Framework > Another Lore' 카테고리의 다른 글

언젠가...  (0) 2009.08.14
XUL 단상  (0) 2009.07.15
read & write  (0) 2009.06.25
최근에 책을 읽다가..  (0) 2009.06.11
AL : Permission  (1) 2009.04.30
Posted by bleujin