|
| 1 | +# 독자적인 할당기 사용하기 |
| 2 | +[출처](http://www.6809.net/tenk/?%E9%9B%91%E8%A8%98%2f2012-12-14 ) |
| 3 | + |
| 4 | + |
| 5 | +## vector |
| 6 | + |
| 7 | +``` |
| 8 | +template <class Allocator> |
| 9 | +class vector_alloc_holder { |
| 10 | + struct members_holder : public Allocator { |
| 11 | + pointer m_start; |
| 12 | + size_type m_size; |
| 13 | + size_type m_capacity; |
| 14 | + } |
| 15 | +} |
| 16 | +``` |
| 17 | + |
| 18 | +template <class T, class Allocator> |
| 19 | +class vector : vector_alloc_holder <Allocator>; |
| 20 | +(SGI 버전 파생 등) 다른 구현이라고 모든 포인터에서 관리하는 것이 많습니다 만,이 구현은 size, capacity를 수로가 있습니다 (개인적으로 디버깅 할 때 멤버 변수에서 볼 수 있으므로 취향) . |
| 21 | + |
| 22 | +Allocator는 (비 static) 멤버 변수가 없으면 진짜 0 바이트가되도록 struct의 상속되어 있습니다. |
| 23 | +stl의 구현이 약간의 차위는있는 Allocator는 (비 static) 멤버 변수가있는 것이 많습니다 만, C ++ 03의 경우 static 멤버 변수로 갖는 것도 있음이었다 같고, 실제로 그렇게되지 컴파일러도있었습니다. |
| 24 | +C ++ 11에서는 scoped_allocator_adaptor의 수도 있고 Allocator는 (비 static) 멤버 변수 필수 인 것 같습니다. |
| 25 | + |
| 26 | +것으로 C ++ 11면 |
| 27 | + |
| 28 | +// 1 회 N 개의 아로케토 그냥 Allocator |
| 29 | +template <typename T, unsigned N> |
| 30 | +class SampleAllocator1 { |
| 31 | +public : |
| 32 | + typedef T value_type; |
| 33 | + SampleAllocator1 () {} |
| 34 | + T * allocate (unsigned n) {assert (n == N); return reinterpret_cast <T *> (buf_);} |
| 35 | + void deallocate (T *, unsigned) {} |
| 36 | + bool operator == (SampleAllocator1 const &) const {return false;} |
| 37 | + bool operator! = (SampleAllocator1 const &) const {return true;} |
| 38 | +private : |
| 39 | + typedef double buf_t; |
| 40 | + buf_t buf _ [(N * sizeof (T) + sizeof (buf_t) -1) / sizeof (buf_t); |
| 41 | +}; |
| 42 | +// typedef std :: vector <int, SampleAllocator1 <int, 1024>> Int1024Vector; |
| 43 | +typedef boost :: container :: vector <int, SampleAllocator1 <int, 1024>> Int1024Vector; |
| 44 | +Int1024Vector g_intVec; |
| 45 | +같은 흉내를 내도 괜찮을 것 ... 그리고 썼지 만 위는 아마 boost :: container에 의존하고 있습니다. |
| 46 | +(대상 환경에서 malloc 준비 전에 (고정 크기) 컨테이너를 사용하고 .. . 적도 있었다) |
| 47 | + |
| 48 | +↑ |
| 49 | +list |
| 50 | +동결 |
| 51 | +구현은 boost :: intrusive의 정의도 많은 귀찮아서 꽤 端折っ하고 (boost :: container로 아니라) 있음직 한 list 구현 회원의 분위기로 다음 (죄송 후문에서 boost :: container 묻힌 좋은 그렇지만 대처하지 못하고 ...) |
| 52 | + |
| 53 | +class Node_Base { |
| 54 | + Node_Base * next_; |
| 55 | + Node_Base * prev_; |
| 56 | +}; |
| 57 | +template <typename T> |
| 58 | +class Node : Node_Base { |
| 59 | + T value_; |
| 60 | +}; |
| 61 | +template <typename T, class A> |
| 62 | +class List : A { |
| 63 | + ListNode_Base root_; |
| 64 | + size_t size_; |
| 65 | +}; |
| 66 | +꽤 적당하지만 메모리의 분위기가 알면 ... |
| 67 | +↑ |
| 68 | +map, multimap, set multiset |
| 69 | +동결 |
| 70 | +map, set은 대부분 레드 - 블랙 트리 구현 같고, boost :: container는 boost :: intrusive을 사용하고있는 것 같습니다. |
| 71 | +그리고, 이것도 메모리의 분위기 만 ... |
| 72 | + |
| 73 | +class Node_Base { |
| 74 | + Node_Base * left_; |
| 75 | + Node_Base * right_; |
| 76 | + Node_Base * parent_; |
| 77 | + bool balance_; |
| 78 | +}; |
| 79 | +template <typename T> |
| 80 | +class Node : Node_Base { |
| 81 | + T value_; |
| 82 | +}; |
| 83 | +template <typename T> |
| 84 | +class Tree : A { |
| 85 | + Node_Base root_; |
| 86 | + size_t size_; |
| 87 | +}; |
| 88 | + |
| 89 | +List와 Map은 전달 된 할당을 그대로 사용하는 것이 아니라, allocator의 회원 |
| 90 | +template <class U> struct rebind {typedef allocator <U> other;}; |
| 91 | +를 사용하여 T 아니라 Node <T> allocator를 사용하고 있습니다. 또한 allocator에 대한 요청은 일반적으로 1 개 단위가된다고 생각합니다. |
| 92 | + |
| 93 | +것으로, list 나 map의 나는 나 할당은 노드 크기 고정 메모리를 풀 두는라는 것이있을 수 있습니다. |
| 94 | + |
| 95 | +template <unsigned B, unsigned N> |
| 96 | +class SampleAlloc2 { |
| 97 | +public : |
| 98 | + SampleAlloc2 () { |
| 99 | + for (unsigned i = 0; i <N-1; ++ i) |
| 100 | + buf_ [i] .link = & buf_ [i + 1]; |
| 101 | + buf_ [N-1] .link = NULL; |
| 102 | + root_ = & buf_ [0]; |
| 103 | + } |
| 104 | + void * allocate (unsigned n) { |
| 105 | + assert (n == 1 && root_); |
| 106 | + Link * p = root_; |
| 107 | + root_ = p-> link; |
| 108 | + return p; |
| 109 | + } |
| 110 | + void deallocate (void * t, unsigned n) { |
| 111 | + if (t) { |
| 112 | + assert (n == 1); |
| 113 | + Link * p = reinterpret_cast <Link *> (t); |
| 114 | + p-> link = root_; |
| 115 | + root_ = p; |
| 116 | + } |
| 117 | + } |
| 118 | +private : |
| 119 | + union Link { |
| 120 | + Link * link; |
| 121 | + char b [B]; |
| 122 | + }; |
| 123 | +private : |
| 124 | + Link * root_; |
| 125 | + Link buf_ [N]; |
| 126 | +}; |
| 127 | +enum {ELM_SIZE = 32}; // 컨테이너 노드를 포함하는 요소의 바이트 수. |
| 128 | +enum {MAX_SIZE = 16}; |
| 129 | +template <typename T> |
| 130 | +class SampleAllocator2 : public SampleAlloc2 <ELM_SIZE, MAX_SIZE> { |
| 131 | + typedef SampleAlloc2 <ELM_SIZE, MAX_SIZE> base_type; |
| 132 | +public : |
| 133 | + typedef T value_type; |
| 134 | + SampleAllocator2 () {BOOST_STATIC_ASSERT (sizeof (T) <= ELM_SIZE);} |
| 135 | + T * allocate (unsigned n) {return reinterpret_cast <T *> (base_type :: allocate (n));} |
| 136 | + void deallocate (T * t, unsigned n) {base_type :: deallocate (t, n);} |
| 137 | + bool operator == (SampleAllocator2 const &) const {return false;} |
| 138 | + bool operator! = (SampleAllocator2 const &) const {return true;} |
| 139 | +}; |
| 140 | +// list |
| 141 | +typedef SampleAllocator2 <int> IntAllocator; |
| 142 | +typedef boost :: container :: list <int, IntAllocator> IntList; |
| 143 | +// map |
| 144 | +struct cstr_less { |
| 145 | + bool operator () (const char * l, const char * r) const {return strcmp (l, r) <0;} |
| 146 | +}; |
| 147 | +typedef std :: pair <char const * const, int> smpPair_t; |
| 148 | +typedef SampleAllocator2 <smpPair_t> SmpPairAllocator; |
| 149 | +typedef boost :: container :: map <const char *, int, cstr_less, SmpPairAllocator> StrIntMap; |
| 150 | +※ 요소 (노드) 크기 (ELM_SIZE)이 직접 집에서 품위입니다 만, 우선은. |
| 151 | + |
| 152 | +↑ |
| 153 | +deque |
| 154 | +동결 |
| 155 | +(구현 번거 로움과 대상에서 그다지 사용하지 않고 ...하지만 마감 시간. 나중에 뭔가) |
| 156 | + |
| 157 | +↑ |
| 158 | +string |
| 159 | +동결 |
| 160 | +std :: string은 (C ++ 03에서) 라이브러리에 대해 상당히 구현이 바라케 있습니다 (EffectiveSTL에 실리고 라든지). c ++ 11에서 속박이 바짝 되었기 때문에 다소으로 다니고 올지도 만, 그래도 여러가지 할 수있을 것입니다. |
| 161 | + |
| 162 | +최소한으로 vector와 같은 내용이 될 것입니다 ... boost :: container :: string에서 端折っ을 뽑아 보면 |
| 163 | + |
| 164 | + struct long_t { |
| 165 | + size_type is_short : 1; |
| 166 | + size_type length : (sizeof (size_type) * CHAR_BIT - 1); |
| 167 | + size_type storage; |
| 168 | + pointer start; |
| 169 | + }; |
| 170 | + struct short_header { |
| 171 | + unsigned char is_short : 1; |
| 172 | + unsigned char length : (CHAR_BIT - 1); |
| 173 | + }; |
| 174 | + struct short_t { |
| 175 | + short_header h; |
| 176 | + value_type data [UnalignedFinalInternalBufferChars]; |
| 177 | + // UnalignedFinalInternalBufferChars ≒ (sizeof (long_t) -sizeof (short_header)) / sizeof (value_type) |
| 178 | + }; |
| 179 | + union repr_t { |
| 180 | + long_raw_t r; |
| 181 | + short_t s; |
| 182 | + }; |
| 183 | +long_t 긴 문자열 할당을 사용하는 경우, sizeof (long_t) -α 이내에 가라 앉는다 짧은 경우 short_t과 같이 영역을 버퍼로 사용 동적 확보하지 않고 끝내야한다는 궁리가되고 있습니다. 64bit CPU라고 vector 호환도 sizeof (포인터) * 3 = 24bytes합니다, 상당히 고마운 구현입니다. (최근 만들고 있었던 나는 내가 string이 바로이 유형이었다 ... 또 boost 때문에 자주) |
| 184 | + |
| 185 | +※ 참고 : 비트 필드는 엔디 관계없이 먼저 쓰여진 것이 낮은 주소에 배치된다. |
| 186 | + |
| 187 | +↑ |
| 188 | +stable_vector, flat_map, flat_set |
| 189 | +동결 |
| 190 | +stl 컨테이너의 대용품이 아니라 vector 구현을 기반으로 한 특화 버전. |
| 191 | +stable_vector는 vector <T *>와 요소를 별도 아로케토 한 것. (boost :: ptr_vector의 유사품?) |
| 192 | +flat_map, flat_set는 vector <T> (map은 T = pair <KEY, VALUE>)에 정렬 상태로 등록하여 인터페이스가 map과 set가 된 것. |
| 193 | +각자 나는 나 컨테이너를 만들어 것 같은 종류이므로 (그래 만들고있었습니다), boost에 있으면 편하게 될 것입니다. (있다면 stable_vector을 flat_map 변한 것도 있고 즐거운 곳) |
| 194 | + |
| 195 | +자세한 것은 다른 사이트에 임하고 있습니다. |
| 196 | + |
| 197 | +↑ |
| 198 | +scoped_allocator_adaptor |
| 199 | +동결 |
| 200 | +C ++ 11에서 늘어난 할당합니다. |
| 201 | +scoped_allocator_adaptor을 이용하면, 예를 들면 map <string, int> 같은 컨테이너에서 로컬 할당을 string과 map에서 공통으로 사용할 수 있도록 할 수 있습니다. |
| 202 | +※ 내부 컨테이너 (이 경우 string) 생성자로 기본 생성자가 아닌 할당을 인수에 취하는 생성자가 사용되게됩니다. |
| 203 | + |
| 204 | +할당을 컨테이너의 멤버로하기 때문에, 당연히 메모리 사용량도 증가 (특히 안쪽의 컨테이너는 수가 많아 쉽게). 때문에 컨테이너의 구성원 할당은 포인터 만하고 구현뿐만 아니라 그 준비, 같은 느낌으로하게 될 것입니다. |
| 205 | + |
| 206 | +자세한 것은 이쪽 의 사이트 라든지 보여달라고하는 편이 좋습니다. |
| 207 | + |
| 208 | +솔직히보기에 잘 몰라서 시도 어쩐지 납득 상태입니다. |
| 209 | + |
| 210 | +이하, 시도 소스. |
| 211 | + |
| 212 | +#include <stdio.h> |
| 213 | +#include <boost / container / map.hpp> |
| 214 | +#include <boost / container / string.hpp> |
| 215 | +#include <boost / container / scoped_allocator.hpp> |
| 216 | +#include <boost / foreach.hpp> |
| 217 | + |
| 218 | +// map에서 해제하지 않는 것이 전제 스택에서 해방 무시 간이 할당. |
| 219 | +template <unsigned N> |
| 220 | +class SampleAlloc3 { |
| 221 | +public : |
| 222 | + SampleAlloc3 () : size_ (0) {} |
| 223 | + void * allocate (unsigned n) { |
| 224 | + assert (size_ + n <= N); |
| 225 | + void * p = ptr (size_); |
| 226 | + size_ + = n; |
| 227 | + return p; |
| 228 | + } |
| 229 | + void deallocate (void *, unsigned) {/ * dummy * /} |
| 230 | +private : |
| 231 | + typedef double buf_t; |
| 232 | + void * ptr (unsigned n = 0) {return (char *) buf_ + ((n + sizeof (buf_t) -1) & ~ (sizeof (buf_t) -1));} |
| 233 | +private : |
| 234 | + unsigned size_; |
| 235 | + buf_t buf_ [N / sizeof (buf_t); |
| 236 | +}; |
| 237 | + |
| 238 | +// map <string, int> 메모리 |
| 239 | +template <unsigned SAMPLE_ALLOC3_SIZE> |
| 240 | +class Hoge { |
| 241 | +public : |
| 242 | + Hoge () : strIntMap_ (std :: less <String> () MapAllocator (StrIntPairAlloc (& smpAlc3_))) {} |
| 243 | + void insert (const char * name, int val) { |
| 244 | + // strIntMap_ [String (name, & smpAlc3_)] = val; |
| 245 | + strIntMap_.emplace (name, val); |
| 246 | + } |
| 247 | + void printAll () { |
| 248 | + BOOST_FOREACH (StrIntPair p, strIntMap_) |
| 249 | + printf ( "% s = % d \ n", p.first.c_str () p.second); |
| 250 | + } |
| 251 | +private : |
| 252 | + typedef SampleAlloc3 <SAMPLE_ALLOC3_SIZE> Alloc; |
| 253 | + template <typename T> |
| 254 | + class SampleAllocator3 { |
| 255 | + public : |
| 256 | + typedef T value_type; |
| 257 | + // SampleAllocator3 () : alc_ (0) {} // 기본 생성자는 제공하지 않는 편이 안전합니다. |
| 258 | + SampleAllocator3 (Alloc * alc) : alc_ (alc) {} |
| 259 | + template <typename U> SampleAllocator3 (SampleAllocator3 <U> const & r) : alc_ (r.detail_get_ptr ()) {} |
| 260 | + T * allocate (unsigned n) {return reinterpret_cast <T *> (alc _> allocate (n * sizeof (T)));} |
| 261 | + void deallocate (T * p, unsigned n) {alc _> deallocate (p, n * sizeof (T));} |
| 262 | + bool operator == (SampleAllocator3 const &) const {return false;} |
| 263 | + bool operator! = (SampleAllocator3 const &) const {return true;} |
| 264 | + Alloc * detail_get_ptr () const {return alc_;} |
| 265 | + private : |
| 266 | + Alloc * alc_; |
| 267 | + }; |
| 268 | + typedef boost :: container :: basic_string <char std :: char_traits <char> SampleAllocator3 <char>> String; |
| 269 | + typedef std :: pair <const String, int> StrIntPair; |
| 270 | + typedef SampleAllocator3 <StrIntPair> StrIntPairAlloc; |
| 271 | + typedef boost :: container :: scoped_allocator_adaptor <StrIntPairAlloc> MapAllocator; |
| 272 | + typedef boost :: container :: map <String, int std :: less <String> MapAllocator> StrIntMap; |
| 273 | +private : |
| 274 | + SampleAlloc3 <SAMPLE_ALLOC3_SIZE> smpAlc3_; |
| 275 | + StrIntMap strIntMap_; |
| 276 | +}; |
| 277 | + |
| 278 | +void sample3 () |
| 279 | +{ |
| 280 | + Hoge <1024> hoge; |
| 281 | + hoge.insert ( "xyz", 1); |
| 282 | + hoge.insert ( "abcdefghijklmnopqrstuvwxyz", 2); |
| 283 | + hoge.insert ( "1234567890", 3); |
| 284 | + hoge.printAll (); |
| 285 | +} |
| 286 | +emplace, emplace_back 단지 (복사) 생성자의 빈도 낮게뿐만 아니라, 이번 같은 경우 바로 필수 종류였습니다. |
| 287 | +공통으로 사용되는 할당 (SampleAllocator3) 디폴트 생성자를 붙여 안돼,라는 것을 실감. |
| 288 | +(기본 생성자 붙이고있는 경우 insert 나 push_back 사용해도 컴파일 통과하기 ... 빠졌습니다) |
0 commit comments