@@ -38,19 +38,17 @@ namespace mdtl = metall::mtlldetail;
38
38
39
39
namespace obcdetail {
40
40
41
- // / A cache block is a unit of memory that contains cached objects
42
- // / (specifically, a cached object is difference_type).
43
- // / Cache blocks are members of two linked-lists.
44
- // / One is a linked-list of all cache blocks in the cache.
45
- // / The other is a linked-list of cache blocks in the same bin.
46
- // / The linked-lists are used to manage the order of cache blocks.
47
- // / The order of cache blocks is used to determine which cache block is
48
- // / evicted when the cache is full.
41
+ // / A cache block contains offsets of cached objects of the same bin (object
42
+ // / size). The maximum number of objects in a cache block is 'k_capacity'. Cache
43
+ // / blocks compose two double-linked lists: 1) a linked list of cache blocks of
44
+ // / the same bin. 2) a linked list of cache blocks of any bin.
49
45
template <typename difference_type, typename bin_no_type>
50
46
struct cache_block {
51
47
static constexpr unsigned int k_capacity = 64 ;
52
48
49
+ // Disable them to avoid unexpected calls.
53
50
cache_block () = delete ;
51
+ ~cache_block () = delete ;
54
52
55
53
inline void clear () {
56
54
bin_no = std::numeric_limits<bin_no_type>::max ();
@@ -96,10 +94,10 @@ struct cache_block {
96
94
difference_type cache[k_capacity];
97
95
};
98
96
99
- // / A bin header is a unit of memory that contains information about a bin
100
- // / within a cache. Specifically, it contains the active block and the number of
101
- // / objects in the active block. The active block is the block that is currently
102
- // / used to cache objects. Non-active blocks are always full.
97
+ // / A bin header contains a pointer to the active block of the corresponding bin
98
+ // / and the number of objects in the active block.
99
+ // / Inserting and removing objects are done only to the active block. Non-active
100
+ // / blocks are always full.
103
101
template <typename difference_type, typename bin_no_type>
104
102
class bin_header {
105
103
public:
@@ -112,7 +110,7 @@ class bin_header {
112
110
m_active_block = nullptr ;
113
111
}
114
112
115
- // Move the active block to the next block
113
+ // Move the active block to the next (older) block
116
114
inline void move_to_next_active_block () {
117
115
if (!m_active_block) return ;
118
116
m_active_block = m_active_block->bin_older_block ;
@@ -147,14 +145,12 @@ class bin_header {
147
145
const cache_block_type *m_active_block{nullptr };
148
146
};
149
147
150
- // / A free blocks list is a linked-list of free blocks.
151
- // / It is used to manage free blocks.
152
- // / Cache blocks are located in a contiguous memory region.
153
- // / All cache blocks are uninitialized at the beginning ---
154
- // / thus, they do not consume physical memory.
155
- // / This free list is designed such that it does not touch uninitialized blocks
156
- // / until they are used. This design is crucial to reduce Metall manager
157
- // / construction time.
148
+ // / A free blocks list contains a linked-list of free blocks.
149
+ // / This class assumes that A) bocks are located in a contiguous memory region
150
+ // / and B) all cache blocks are uninitialized at the beginning so that they do
151
+ // / not consume physical memory. This free list is designed such that it does
152
+ // / not touch uninitialized blocks until they are used. This design is crucial
153
+ // / to reduce Metall manager construction time.
158
154
template <typename difference_type, typename bin_no_type>
159
155
class free_blocks_list {
160
156
public:
@@ -207,14 +203,13 @@ class free_blocks_list {
207
203
// Blocks that were used and became empty
208
204
const cache_block_type *m_blocks;
209
205
// The top block of the uninitialized blocks.
210
- // Uninitialized blocks are located in a contiguous memory region.
211
206
const cache_block_type *m_uninit_top;
212
207
const cache_block_type *m_last_block;
213
208
};
214
209
215
- // / A cache header is a unit of memory that contains information about a cache.
216
- // / Specifically, it contains the total size of objects in the cache,
217
- // / the oldest and newest active blocks, and a free blocks list.
210
+ // / A cache header contains some metadata of a single cache.
211
+ // / Specifically, it contains the total size (byte) of objects in the cache,
212
+ // / the pointers to the oldest and the newest blocks, and a free blocks list.
218
213
template <typename difference_type, typename bin_no_type>
219
214
struct cache_header {
220
215
private:
@@ -231,24 +226,24 @@ struct cache_header {
231
226
232
227
void clear () {
233
228
m_total_size_byte = 0 ;
234
- m_oldest_active_block = nullptr ;
235
- m_newest_active_block = nullptr ;
229
+ m_oldest_block = nullptr ;
230
+ m_newest_block = nullptr ;
236
231
m_free_blocks.clear ();
237
232
}
238
233
239
234
inline void unregister (const cache_block_type *const block) {
240
- if (block == m_newest_active_block ) {
241
- m_newest_active_block = block->older_block ;
235
+ if (block == m_newest_block ) {
236
+ m_newest_block = block->older_block ;
242
237
}
243
- if (block == m_oldest_active_block ) {
244
- m_oldest_active_block = block->newer_block ;
238
+ if (block == m_oldest_block ) {
239
+ m_oldest_block = block->newer_block ;
245
240
}
246
241
}
247
242
248
243
inline void register_new_block (const cache_block_type *const block) {
249
- m_newest_active_block = block;
250
- if (!m_oldest_active_block ) {
251
- m_oldest_active_block = block;
244
+ m_newest_block = block;
245
+ if (!m_oldest_block ) {
246
+ m_oldest_block = block;
252
247
}
253
248
}
254
249
@@ -258,20 +253,20 @@ struct cache_header {
258
253
return m_total_size_byte;
259
254
}
260
255
261
- inline cache_block_type *newest_active_block () noexcept {
262
- return const_cast <cache_block_type *>(m_newest_active_block );
256
+ inline cache_block_type *newest_block () noexcept {
257
+ return const_cast <cache_block_type *>(m_newest_block );
263
258
}
264
259
265
- inline const cache_block_type *newest_active_block () const noexcept {
266
- return m_newest_active_block ;
260
+ inline const cache_block_type *newest_block () const noexcept {
261
+ return m_newest_block ;
267
262
}
268
263
269
- inline cache_block_type *oldest_active_block () noexcept {
270
- return const_cast <cache_block_type *>(m_oldest_active_block );
264
+ inline cache_block_type *oldest_block () noexcept {
265
+ return const_cast <cache_block_type *>(m_oldest_block );
271
266
}
272
267
273
- inline const cache_block_type *oldest_active_block () const noexcept {
274
- return m_oldest_active_block ;
268
+ inline const cache_block_type *oldest_block () const noexcept {
269
+ return m_oldest_block ;
275
270
}
276
271
277
272
inline free_blocks_list_type &free_blocks () noexcept { return m_free_blocks; }
@@ -282,20 +277,28 @@ struct cache_header {
282
277
283
278
private:
284
279
std::size_t m_total_size_byte;
285
- const cache_block_type *m_oldest_active_block {nullptr };
286
- const cache_block_type *m_newest_active_block {nullptr };
280
+ const cache_block_type *m_oldest_block {nullptr };
281
+ const cache_block_type *m_newest_block {nullptr };
287
282
free_blocks_list_type m_free_blocks;
288
283
};
289
284
290
- // / A cache container is a unit of memory that contains all data structures that
291
- // / constitute a cache.
285
+ // / A cache container contains all data that constitute a cache.
286
+ // / This cache container holds a cache header, bin headers, and cache blocks in
287
+ // / a contiguous memory region.
292
288
template <typename difference_type, typename bin_no_type,
293
289
std::size_t max_bin_no, std::size_t num_blocks_per_cache>
294
290
struct cache_container {
295
291
using cache_heaer_type = cache_header<difference_type, bin_no_type>;
296
292
using bin_header_type = bin_header<difference_type, bin_no_type>;
297
293
using cacbe_block_type = cache_block<difference_type, bin_no_type>;
298
294
295
+ // Disable the default constructor to avoid unexpected initialization.
296
+ cache_container () = delete ;
297
+
298
+ // Disable the copy constructor to avoid unexpected destructor call.
299
+ ~cache_container () = delete ;
300
+
301
+ // This data structure must be initialized first using this function.
299
302
void init () {
300
303
new (&header) cache_heaer_type (blocks, num_blocks_per_cache);
301
304
// Memo: The in-place an array construction may not be supported by some
@@ -381,7 +384,12 @@ inline constexpr std::size_t comp_num_blocks_per_cache(
381
384
382
385
} // namespace obcdetail
383
386
384
- // / \brief A cache for small objects.
387
+ // / A cache for small objects.
388
+ // / This class manages per-'CPU' (i.e., CPU-core rather than 'socket') caches
389
+ // / internally. Actually stored data is the offsets of cached objects. This
390
+ // / cache push and pop objects using a LIFO policy. When the cache is full
391
+ // / (exceeds a pre-defined threshold), it deallocates some oldest objects first
392
+ // / before caching new ones.
385
393
template <typename _size_type, typename _difference_type,
386
394
typename _bin_no_manager, typename _object_allocator_type>
387
395
class object_cache {
@@ -469,7 +477,7 @@ class object_cache {
469
477
object_cache &operator =(const object_cache &) = default ;
470
478
object_cache &operator =(object_cache &&) noexcept = default ;
471
479
472
- // / \brief Pop an object offset from the cache.
480
+ // / Pop an object offset from the cache.
473
481
// / If the cache is empty, allocate objects and cache them first.
474
482
difference_type pop (const bin_no_type bin_no,
475
483
object_allocator_type *const allocator_instance,
@@ -479,8 +487,8 @@ class object_cache {
479
487
deallocator_function);
480
488
}
481
489
482
- // / \brief Cache an object.
483
- // / If the cache is full, deallocate some cached objects first.
490
+ // / Cache an object.
491
+ // / If the cache is full, deallocate some oldest cached objects first.
484
492
// / Return false if an error occurs.
485
493
bool push (const bin_no_type bin_no, const difference_type object_offset,
486
494
object_allocator_type *const allocator_instance,
@@ -489,7 +497,7 @@ class object_cache {
489
497
deallocator_function);
490
498
}
491
499
492
- // / \brief Clear all cached objects.
500
+ // / Clear all cached objects.
493
501
// / Cached objects are going to be deallocated.
494
502
void clear (object_allocator_type *const allocator_instance,
495
503
object_deallocate_func_type deallocator_function) {
@@ -544,6 +552,10 @@ class object_cache {
544
552
}
545
553
546
554
private:
555
+ struct free_deleter {
556
+ void operator ()(void *const p) const noexcept { std::free (p); }
557
+ };
558
+
547
559
inline static unsigned int priv_get_num_cpus () {
548
560
return mdtl::get_num_cpus ();
549
561
}
@@ -565,7 +577,7 @@ class object_cache {
565
577
#endif
566
578
}
567
579
568
- // / \brief Get CPU number.
580
+ // / Get CPU number.
569
581
// / This function does not call the system call every time as it is slow.
570
582
inline static size_type priv_get_cpu_no () {
571
583
#ifdef METALL_DISABLE_CONCURRENCY
@@ -646,7 +658,7 @@ class object_cache {
646
658
new_block->cache );
647
659
648
660
// Link the new block to the existing blocks
649
- new_block->link_to_older (cache_header.newest_active_block (),
661
+ new_block->link_to_older (cache_header.newest_block (),
650
662
bin_header.active_block ());
651
663
652
664
// Update headers
@@ -697,7 +709,7 @@ class object_cache {
697
709
assert (free_block);
698
710
free_block->clear ();
699
711
free_block->bin_no = bin_no;
700
- free_block->link_to_older (cache_header.newest_active_block (),
712
+ free_block->link_to_older (cache_header.newest_block (),
701
713
bin_header.active_block ());
702
714
cache_header.register_new_block (free_block);
703
715
bin_header.update_active_block (free_block, 0 );
@@ -726,7 +738,7 @@ class object_cache {
726
738
// Make sure that the cache has enough space to allocate objects.
727
739
while (total_size + new_objects_size > k_max_per_cpu_cache_size ||
728
740
free_blocks.empty ()) {
729
- auto *const oldest_block = cache_header.oldest_active_block ();
741
+ auto *const oldest_block = cache_header.oldest_block ();
730
742
assert (oldest_block);
731
743
732
744
// Deallocate objects from the oldest block
@@ -755,10 +767,10 @@ class object_cache {
755
767
#ifdef METALL_ENABLE_MUTEX_IN_OBJECT_CACHE
756
768
std::vector<mutex_type> m_mutex;
757
769
#endif
758
- std::unique_ptr<cache_storage_type[]> m_cache{nullptr };
770
+ std::unique_ptr<cache_storage_type[], free_deleter > m_cache{nullptr };
759
771
};
760
772
761
- // const_bin_iterator
773
+ // / An iterator to iterate over cached objects of the same bin.
762
774
template <typename _size_type, typename _difference_type,
763
775
typename _bin_no_manager, typename _object_allocator_type>
764
776
class object_cache <_size_type, _difference_type, _bin_no_manager,
0 commit comments