스택 메모리 상의 placement new

jaychoi의 이미지

임의의 함수 객체를 저장하는 클래스를 작성하였습니다. 임의의 함수 객체의 크기는 예측할 수 없기 때문에 원래의 코드에서는 함수 객체의 크기만큼 힙 메모리를 할당한 후에 함수 객체를 복사하여 저장하였습니다. 그런데 힙 메모리 할당이 특정 상황(!) 에서는 성능 저하에 영향을 줄 수 있기 때문에 메타 템플릿 기법을 사용하여 특정 크기( 2 * sizeof(void *) )의 unsigned char 버퍼를 멤버로 선언하고 함수 객체의 크기가 내장 버퍼 사이즈 보다 작을 경우에는 특별히 placement new 를 사용하여 힙 메모리 할당을 하지 않음으로써 속도 향상을 얻고자 했습니다. 실재 위의 클래스를 적용하는 환경에서 임의의 클래스 T가 간단한 prediate 이거나 단순히 free 함수를 감싸고 있는 경우가 대부분 인지라서 기대 이상의 속도 향상을 예상하고 있었습니다.

그러다가 C++ 표준에 호환하는 코드를 작성하고자 한다면 placement new 를 스택 메모리에 할당 된 버퍼에 사용하면 안된다는 것을 조금 뒤늦게서야 깨우치게 되었습니다. ( http://www.devx.com/tips/Tip/12756 또는 http://www.parashift.com/c++-faq-lite/dtors.html#faq-11.10 ). 여기 포럼을 검색한 후 정렬 제한 때문에 비인텔 계열의 CPU에서는 BUS 에러가 발생한다는 사실을 어렴풋이 알게 되었습니다.

인터넷을 조금 더 검색하다 보니 다음의 포럼에서 http://www.dbforums.com/showthread.php?t=1556368 끝 부분에서 올라온 답변에 Frederick Gotham란 사람이 세 가지 대안 방법을 제시 하였습니다.

(1) Dynamically allocating it
(2) Using union trickery
(3) Using boost's "align_for" (or whatever it's called)

세 번째 boost 의 경우는 type traits 라이브러리 중 alignment_of, aligned_storage, type_with_alignment 가 이 사람이 제시한 방법을 의미하는 것 같습니다. 반면에 두 번째 공용체 트릭이라는게 어떤 것인지 검색해 봤는데 찾을 수가 없었습니다.

정렬 제한이 위의 경우에 어떻게 적용되는지 정확하게 개념이 서지 않고 있습니다. 자세한 설명과 함께 사용 가능한 (그러면서 표준에 부합하는 방법이면 좋겠지만 그렇지 않다면 비인텔 계열에서도 문제가 발생하지 않는) 방법을 알려주시면 정말로 감사 드리겠습니다.

struct functor_ptr_holder
{
  union
  {
    void * ptr_;  //!< Pointer to the function object allocated on the heap memory.
    unsigned char buf_[2 * sizeof( void * )]; //!< Buffer to store the function object if the size fits.
 
  };  // union
 
  typedef bool (*typeless_deleter_stub)(functor_ptr_holder &);
  typeless_deleter_stub deleter_stub_;  //!< Deleter stub.
 
  template<class T> struct on_stack_
  {
    inline static void init(functor_ptr_holder & fph, T * ptr)
    {
      new ( fph.buf_ ) T( *ptr );
      fph.deleter_stub_ = &on_stack_<T>::deleter;
    }
 
    inline static void deleter(functor_ptr_holder & fph)
    {
      if( ptr ) reinterpret_cast<T *>( &fph.buf_ )->~T();
    }
 
  };  // template<class T> struct on_stack_
 
  template<class T> struct on_heap_
  {
    inline static void init(functor_ptr_holder & fph, T * ptr)
    {
      fph.ptr_ = new T( *ptr );
      fph.deleter_stub_ = &on_heap_<T>::deleter;
    }
 
    inline static void deleter(functor_ptr_holder & fph)
    {
      if( ptr ) delete static_cast<T *>( fph.ptr_ );
    }
 
  };  // template<class T> struct on_heap_
 
  template<class T> struct select_
  {
    typedef on_stack_<T>  then_type;
    typedef on_heap_<T>   else_type;
 
    typedef typename if_< sizeof( T ) <= buffer_size, then_type, else_type >::type type;
 
  };  // template<class T> struct select_
 
  // --------------------------------------------------------------------------------
 
  functor_ptr_holder() : ptr_( 0 ), deleter_stub_( 0 )
  {
  }
 
  template<class T>
  functor_ptr_holder(T * ptr) : ptr_( 0 ), deleter_stub_( 0 )
  {
    select_<T>::type::init( *this, ptr );
  }
 
};  // struct functor_ptr_holder

thyoo의 이미지

관련된 링크입니다.

http://www.gotw.ca/gotw/028.htm
___________________________________
Less is More (Robert Browning)

___________________________________
Less is More (Robert Browning)

jaychoi의 이미지

링크 정말 감사드립니다. 원하는 답변이 설명 되어있습니다. boost 의 alignment_of 등등의 구현도 조금 더 이해하기가 수월해졌습니다. 예전에 gotw 시리즈 한번 훝어보면서 봤었던 내용인데 막상 필요해서 찾을때는 기억을 못해냈네요.

감사합니다.

jaychoi의 이미지

일반 함수 또는 멤버 함수를 저장할 수 있는 C++ delegate 클래스를 작성한적이 있습니다. 힙 메모리 할당을 하지 않기 위해서 위에서와 마찬가지로 char 배열 버퍼를 멤버로 잡고서 placement new를 사용하여 boost::function 보다 수 배 빠른 성능 향상을 얻을 수 있었습니다만 이제서야 이와 같은 접근이 정렬 제한을 만족하지 못하고 따라서 특정 환경에서 버스 오류를 발생시킬 수 있다는 것을 알게 되었습니다.

위의 GOTW 에서 설명된 공용체 트릭을 조금 변경해서 다음과 같이 수정하면 표준에 위배되지 않는다고 할 수 있을지 알고 싶습니다. "함수를 내장 char 버퍼에 저장한다는 특수한 상황"이기 때문에 다음과 같은 공용체를 사용함으로써 표준에 완벽하게 부합한다고 생각합니다.

class alignment_dummy_base1 { };
class alignment_dummy_base2 { };
 
class alignment_dummy1 : alignment_dummy_base1 { };                        // single inheritance.
class alignment_dummy2 : alignment_dummy_base1, alignment_dummy_base2 { }; // multiple inheritance.
class alignment_dummy3 : virtual alignment_dummy_base1 { };                // virtual inheritance.
class alignment_dummy4;                                                    // unknown (incomplete).
 
typedef void (*function_ptr)(); // free function pointer
typedef int (alignment_dummy1::*member_function_ptr1)(); // member function pointer of single inheritance class.
typedef int (alignment_dummy2::*member_function_ptr2)(); // member function pointer of multiple inheritance class.
typedef int (alignment_dummy3::*member_function_ptr3)(); // member function pointer of virtual inheritance class.
typedef int (alignment_dummy4::*member_function_ptr4)(); // member function pointer of unknown (incomplete) class.
 
enum
{
  is_multiple_less_than_virtual = sizeof( member_function_ptr2 ) < sizeof( member_function_ptr3 ),
  is_multiple_less_than_unknown = sizeof( member_function_ptr2 ) < sizeof( member_function_ptr4 ),
};
 
template<bool, typename THEN, typename ELSE> struct if_;
template<typename THEN, typename ELSE> struct if_<true, THEN, ELSE>   { typedef THEN type; };
template<typename THEN, typename ELSE> struct if_<false, THEN, ELSE>  { typedef ELSE type; };
 
union max_align_for_funtion_pointer
{
  void *                dummyvp;
  function_ptr          dummyfp;
  member_function_ptr1  dummymfp1;
  member_function_ptr2  dummymfp2;
  if_< is_multiple_less_than_virtual, member_function_ptr2, member_function_ptr3 >::type dummymfp3;
  if_< is_multiple_less_than_unknown, member_function_ptr2, member_function_ptr4 >::type dummymfp4;
 
};  // union max_align_for_funtion_pointer
 
template<class R, class T1, class T2, ..., class TN>
class delegate
{
private:
  enum { buffer_size = sizeof( max_align_for_funtion_pointer ) };
  union
  {
    max_align_for_funtion_pointer m_;
    void * ptr_;
    unsigned char buf_[ buffer_size ];
  };
};

템플릿 메타 프로그래밍을 사용하여 다중 상속을 받은 클래스의 멤버 함수 포인터의 크기보다 작거나 같은 함수 포인터들은 위와 같이 정의된 내장된 버퍼에 저장하고 크기가 큰 경우에만 힙 메모리 할당을 하여 저장하는 방식을 사용합니다. 코드 프로젝트의 http://www.codeproject.com/cpp/FastDelegate.asp 의 중반에 나온 함수 비교 테이블에 참조 했습니다.

이와 같은 접근 방법이 C++ 표준에 전혀 위배되지 않는다고 말할 수 있을까요?

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.