'boost'에 해당되는 글 5건

  1. 2008.08.25 [pattern] Leader/Follower
  2. 2008.08.25 메모리 풀링
  3. 2008.08.25 [Idioms] Pimpl Idiom
  4. 2008.08.25 [Idioms] Resource Acquisition Is Initialization
  5. 2008.08.22 boost::asio overview
2008. 8. 25. 19:59

[pattern] Leader/Follower

POSA 2에 나오는 패턴이다.

자세한 내용은 책을 보던가, EvaCast 강좌를 보면 내용을 볼수가 있다.

기존의 Thread Pooling이 Master Thread를 두고 Master Thread가 필요할때 마다 Pool에서 하나씩 꺼내서 사용시키는거라고 하는데, 사실 Thread Pool 이라는 것은 사용해 본적도 없고, 사용할 일도 없어서, 기존의 방법이라는게 어떻게 구현되는건지는 잘 모르겠다.

Leader/Follower 패턴을 대략적으로 살펴보면, Thread Poolling의 한 방법으로 Thread들이 생성이 되면, 그 Thread들 중 하나가 Leader가 되고, 남은 Thread들 중 하나가 Follower가 된다. 나머지 Thread들은 대기 상태.
request가 들어오면 Leader는 그 request들을 처리하러 가고, Follower에게 Leader의 자리를 넘긴다, Follower는 Leader가 되면서, 남은 Thread들중 하나에게 Follower의 자리를 넘겨 주고, 대기하는 과정을 반복하는 것이다.
실제 구현을 본적이 없어서, Leader가 Follower을 정하는지 아니면 다른 규칙이 있는지는 알수가 없다.

대신 기존에 사용된다던 Master Thread가 없어져서, 혹시 모를 Master Thread의 객사(Exception 또는 Dead Lock등이겠지)하면서 생기는 불상사가 없어져서 좋단다.

이 패턴이 대표적으로 사용되는 사용되는 곳을 보자면, POSA2에서는 ORB(Object Request Broker)를 설명하면서 Reactor에 대한 Dispatcher로서 이 패턴을 설명하고 있다. Connector와 Acceptor에서 핸들러를 등록하고 Reactor에서 디스패칭을 하는 구조라고 생각할수 있을것이다.

POSA의 결과물인 ACE에서 찾아 보면, ACE_TP_Reactor라는 곳에서 Event Demultiplexer로 이 패턴을 사용한다고 한다. ACE는 살펴볼 기회가 없어서 이거 쓰면서 실제 구현코드를 대충 살펴만 봤다.

그 다음으로는 Proactor를 설명하면서도 이 패턴에 대한 설명이 나오는데, IOCP가 이 패턴을 사용한다고 한다.

사실 이걸 보기 전까지는 이 패턴에 대한 대략적인 개념만 잡혔는데, IOCP가 이 패턴을 사용한다고 하니, 어디서 사용하지라고 생각을 좀 해 보았다.
그렇다!! GetQueuedCompletionPort()(이름이 맞나 모르겠다 -_-) 란 놈 빼고는 사용할때가 없는 거다. 지금까지 생각없이 사용했던 GetQueued~()이 Leader/Follower 패턴을 사용하고 있었던 것이다. 그 생각을 하고 나니, IOCP에 대한 구현이나 실제 이 패턴에 대한 내부 구조도 조금은 이해가 되는거 같다.

생각해 보니, 4~5년전쯤에 빌링서버 만들때, 다수 update message를 여러개의 Thread에서 경쟁을 시켜서 message을 획득하게 만든적이 있었는데, 그거랑 비슷한 방식이다. 그 때는 Thread들을 무한 경쟁을 하게 만들었었는데, 이 패턴은 Leader와 Follower를 지정해 주게 되므로서 어느 정도 순차적인 처리가 가능한거 같다..
(사실 잘 모르겠다. 무한 경쟁 시키는 것과, 이 패턴을 사용하는것중 어느게 더 좋은건지.. 내가 모르는 먼가가 있다는거 같다. 이 패턴의 장점이 쓰레드 생성에 드는 비용 감소(poolling이 되므로!), 와 Context Switching비용이 감소 되는데 있다고 하는데.. Context Switching이 적게 드나 보다. Wait하는 Thread가 있으니까 그런거 같다. 그러고 보니 그런것도 같다...)

어쨋거나, 결론적으로 이 Leader/Follower 패턴을 사용하고 싶은데, 직접 구현하기는 귀찮은 사람은, ACE_TP_Reactor를 사용하거나, IOCP를 이용하면 편하게 사용할수 있을것이다. ACE는 무거워서 사용하기 싫고, IOCP역시 직접 구현하기 싫다면, boost::asio::io_service를 사용하면 된다. 이거 역시 내부적으로는 Proactor(Reactor를 사용할수도 있을거 같은데.. 잘 모르겠다)-Win32환경에서는 IOCP로 구현이 되어 있는 것이다. 우하하!!
그것 조차 싫다면, 사용 안해면 된다.. -_-

밑에 소스는 보너스로, 요즘 공부하고 있는 boost::asio에 나오는 priority_handler 예제이다. 요렇게 쓰면, 나도 모르는 사이에 Leader/Follower 사용 유저가 되어 있는 것이다. :) (priority_handler가 어떤 식으로 돌아가는지 모르겠으면 main부분에서 io_service사용부분만 보면 된다)


2008. 8. 25. 18:00

메모리 풀링

요즘 서버 모듈을 다시 만들어 볼려고 이것 저것 손대 보고 있는데, 오늘은 동적인 message buffer 생성을 위한 메모리 풀을 만들어 보고 테스트를 해 보았다.

결과는 아래와 같다.

테스트 환경:
컴파일러 : vc2008 express edition
PC : Pentium(R)4 CPU 3.00GHz, Ram 2G
운영체제 : XP sp2
테스트 내용 :
  1032BYTE 짜리 buffer, 4byte int 각각 5만개씩 생성
결과 :  
** message_buffer로 테스트 했을 때
미리 잡아 놓고 dequeue를 이용한 풀[message_buffer] : 0
기본 new/delete[message_buffer] : 0.156
boost::object_pool[message_buffer] : 142.844
boost::pool_allocator[message_buffer] : 141.203
boost::fast_pool_allocator[message_buffer] : 0.047
 
** int로 테스트 했을 때
미리 잡아 놓고 dequeue를 이용한 풀[int] : 0.016
기본 new/delete[int] : 0.032
boost::object_pool[int] : 3.875
boost::pool_allocator[int] : 3.89
boost::fast_pool_allocator[int] : 0

생각보다 object_pool 이나 pool_allocator가 성능이 신통치가 않다.(사실 사용법을 잘 몰라서 그런지도 모른다. 대충썼다 -_-)

미리 잡아 놓고 dequeue를 사용한 풀을 써볼려고 테스트를 만든건데, 위의 테스트는 버퍼를 미리 잡는 시간은 제외된 시간이다. 의외로 쓸만한거 같은데, 동기화 오브젝트를 넣고 하면, 얼마나 성능이 나올지는 좀더 테스트를 해 봐야 될거 같다.

아래는 소스 코드

 

 


2008. 8. 25. 12:14

[Idioms] Pimpl Idiom

PImpl Idiom

- 인터페이스(Interface)와 구현(Implementation)을 구분해서, Coupling을 최소화
-
구현(Implementation)을 감출 수 있고, 구현의 세부내용이 변경되더라도, Client에서의 재컴파일을 피하게 할수 있다.

다른 이름

- Compilatioin Firewall
- Cheshire Cat
- Handle body

동기

- 클래스를 작성함에 있어서, 일반적으로 Interface정의하고, 클래스내에 실제 구현 클래스를 선언해서 Interface내부에 사용하게 된다. 이때 해당 구현 클래스의 내용이 변경이 되면, 이 클래스의 인터페이스만 참조만(Dll, Lib) 하는 Client들은 프로젝트 전체를 재 컴파일해야 되는 경우가 발생한다. 또한 클래스간의 의존성(dependency)가 늘어날수록, 작은 변경에도 컴파일 시간이 증가할수 있다.

example)

#Foo.h

#include “impl1.h”   // impl1의 변경되면, impl2.h, foo.h 역시 컴파일이 된다.

#include “impl2.h” // 이역시 마찬가지

Class foo

{

Public:

           Impl2 doSomeThing();

private:

            Impl _impl;

};

 

해결방법

클래스내에 두개의 구현 클래스로 추상화해서 분리한다. 첫번째로 클래스의 Interface와 구분자를 제공하고(이를 Handle이라고 한다), 두번째는 클래스의 실제 구현을 하게 된다(이를 Body라고 한다)

Example)

 

이렇게 Interface와 구현 분리함에 따라, 실제 구현의 내용이 변경되더라도, Interface를 참조하는 Client들의 변경 및 재컴파일이 필요없진다. DLL이나 Lib를 만들어 배포 할 때 좋은 방법이 될수 있다.

 

Comment

이 방법 역시 dependency를 최소화 한다는 의미에서 범용적으로 사용되는 용법이라고 볼수 있다. 그렇다고 무조건 적으로 이 방법이 효율적이라고 볼수 없는 것이 implementationInterfacePointer로 선언이 되어 있기 때문에, 구현부 어딘가에는 new/delete가 있어야 되므로, 이를 관리해야 된다는 부담이 없지 않아 있다. 또한 많이 사용되는 클래스라면, 성능상의 부담 또한 신경써야 된다. 때문에, 일반적으로 STL같은 표준라이브러리와 같이 거의 변경이 되지 않는 Object는 그냥 정의해서 사용하고, 구현 변경이 자주 되거나, 동적으로 변경될수도 있으며, 새로 개발하고 있는 구현들에서는 이 용법을 사용하는 것이 유리할것이라고 생각됩니다. 또한 pImpl을 추상화 시키면, Template Method, Factory Method등의 디자인 패턴에서 일반적으로 볼수 있는 형태가 된다.

 

알려진 사용 예

-       C++ 소프트웨어에서 아주 많이 사용된다.

 

관련된 Idioms

-       Counted Body(PImpl에서 reference counting개념을 넣었다) 위의 코드의 StringRep::count Counted Body에서 사용된다)

-       Smart Pointer(Pointee(라고 하나?)로 사용됨)

-       Envelope Letter(편지봉투라고 하나? -0- ) : PImle에서 구현를 추상화시켜서 사용하는 방식이라고 한다, 봉투(ADT(abstract data type)라고 한다)는 똑 같은데 내용(implementation)이 여러 개가 될수 있다는 건데, 쉽게 Template Method, Factory Method등을 생각하면 편할거 같다)

-       Handle Body Hierarchy : (먼지 모르겠다)

-       Checked delete : (delete의 안정성을 높이는 Idiom이라고 한다. Object type check를 통한 안전한 delete(좀 이상하다…sizeof(T)0이 될수도 있나? -_- ), 런타임이 아니고 컴파일 타임 체커(boost::checked_delete(), boost::check_array_delete()참조..)

-       Fast Pimpl : comment에서 언급한, new/delete의 문제를 해결 거, 동적 생성을 별도의 allocator(memory pool같은걸로)를 만들어 new/delete overloading 해서 사용한다.

 

More C++ Idioms 참고

2008. 8. 25. 10:46

[Idioms] Resource Acquisition Is Initialization

Resource Acquisition Is Initialization(RAII)

- 스코크의 끝에서 자원을 해제를 보장해준다.
-
예외등에서 안전한 해제를 보장해 줄수 있다

동기

- scope(function 또는 function내에서 {} 로 지정된)내에서 new, object locking,file open등과 같은 자원들을 생성한 이후, function내에서 복잡한 분기 후 종료, 예외(exception)등에서 자원을 해제하기가 쉽지 않고, 쉽게 실수 할수 있다. Mutexsemapore같은 동기화 오브젝트의 사용에 있어서는 어플리케이션에 심각한 Dead Lock이 발생할수도 있다.

example)

void foo ()

{

  char * ch = new char [100];

  if (...)

     if (...)

        return;

     else if (...)

            if (...)

  else

     throw "ERROR";

 

  delete [] ch; // 여기에 오지 않으면 Memory leak이 발생한다.

}

void bar ()

{

  lock.acquire();

  if (...)

     if (...)

        return;

  else

     throw "ERROR";

 

  lock.release(); // 여기에 오지 않으면 Dead lock이 발생한다.

}

 

해결방법

scope내에 클래스의 생성자에서 자원을 생성하고, 소멸자에서 자원의 해제를 하게 한다.

 

scope내에서 생성된 Object는 콜스택에 저장이 되고, scope가 종료되면서(exception이 발생했을때도 마찬가지) 소멸자가 호출되는 것을 이용해서 쉽고 편하게 자원 해제를 할수 있으며, 자원 해제의 시점도 조절할수 있다.

Comment

프로젝트 진행시 아마 가장 많이 사용되는 Idiom중의 하나 일거 같다.  아마 이름이나 내용을 모르고도 사용하는 개발자도 많을거라 생각된다.(나도 그랬다.... ;;) 하긴, 모든 Idiom들이 다 그렇치머...
 

알려진 사용 예

-       std::auto_ptr()

-       boost::scoped_ptr

-       boost::mutex::scoped_lock

 

관련된 Idioms

-       Scope Guard (RAII에서 자원의 Release를 사용자가 정할수 있고, 이에 대한 flag를 둠으로서 안전한 자원 해제를 보장하는 방법)

-       Reference Counting( boost::shared_ptr()같이 참조 카운트를 이용한 SmartPtr의 종료 용례를 얘기한거 같은데, 확인해 보니 맞는거 같다. Boost::shared_ptr, std::tr1:;shared_ptr)

-       Scoped Locking(RAII에서 Mutex, Semapore같은 동기화 오브젝트에서만 한정된다)

 

More C++ Idioms 참고

2008. 8. 22. 12:38

boost::asio overview

Supported Platforms

테스트된 플랫폼 및 컴파일러

  • Win32 and Win64 using Visual C++ 7.1 and Visual C++ 8.0.
  • Win32 using MinGW.
  • Win32 using Cygwin. (__USE_W32_SOCKETS must be defined.)
  • Linux (2.4 or 2.6 kernels) using g++ 3.3 or later.
  • Solaris using g++ 3.3 or later.
  • Mac OS X 10.4 using g++ 3.3 or later.

아래 플랫폼에서도 동작될것 같음...

  • AIX 5.3 using XL C/C++ v9.
  • HP-UX 11i v3 using patched aC++ A.06.14.
  • QNX Neutrino 6.3 using g++ 3.3 or later.
  • Solaris using Sun Studio 11 or later.
  • Tru64 v5.1 using Compaq C++ v7.1.
  • Win32 using Borland C++ 5.9.2

 Boost.Asio 사용에 필요한 라이브러리들

  • Boost.System : the boost::system::error_code boost::system::system_error classe사용에 필요함  
  • Boost.Regex (선택) : read_until() 또는 async_read_until() 사용할때
  • OpenSSL (선택) : SSL을 지원하게 할때

설명
boost::asio
C++을 사용하는 system programmer를 위한 운영체제 독립적인 네트웍 라이브러리이다. 특히 아래에 나오는 리스트들을 목표로한다.

  • Portability(이식성)
  • Scalability(확장성)
  • Efficiency(효율성)
  • Model Berkeley sockets(버클리 소켓 모델)
  • Ease of use(사용성)
  • Basis for further abstraction(미래의 추상화에 대한 기초

** 기본적으로 boost::asio는 boost를 활용한 async 기반의 네트웍 라이브러리이다, ACE라는 아주 유명한 네트웍 라이브러리가 있지만, 충분한 이론적인 내공이 뒷받침 되지 않는 이상 사용하기도 힘들고, 무겁다는 느낌이 든다. 또한 boost의 경우 프로젝트 전반에 걸쳐 아주 유용하게 사용되지만, ACE는 네트웍 프로그래밍을 하지 않는 이상 사용할 일이 별로 없다. 또한 boost와 충돌되는 부분도 많고(networking(-_-), threading, sync object..등) 무엇보다 사용법을 익히기가 쉬우며, 다른 boost라이브러리들과의 찰떡 궁합(특히나, boost::bind)의 위력을 과시 함으로 인해서, ACE을 대체 하기 위한 수단으로도 나름 나쁘지는 않을것이라 생각된다.