@@ -95,13 +95,13 @@ class TransferCache {
9595
9696 TransferCache (Manager *owner, int size_class, Capacity capacity,
9797 bool use_all_buckets_for_few_object_spans)
98- : owner_(owner),
99- lock_ (absl::kConstInit , absl::base_internal::SCHEDULE_KERNEL_ONLY),
100- max_capacity_(capacity.max_capacity),
101- slot_info_(SizeInfo({0 , capacity.capacity })),
98+ : lock_(absl::kConstInit , absl::base_internal::SCHEDULE_KERNEL_ONLY),
10299 low_water_mark_ (0 ),
100+ slot_info_(SizeInfo({0 , capacity.capacity })),
103101 slots_(nullptr ),
104- freelist_do_not_access_directly_() {
102+ freelist_do_not_access_directly_(),
103+ owner_(owner),
104+ max_capacity_(capacity.max_capacity) {
105105 freelist ().Init (size_class, use_all_buckets_for_few_object_spans);
106106 slots_ = max_capacity_ != 0 ? reinterpret_cast <void **>(owner_->Alloc (
107107 max_capacity_ * sizeof (void *)))
@@ -363,39 +363,53 @@ class TransferCache {
363363 slot_info_.store (info, std::memory_order_relaxed);
364364 }
365365
366- Manager *const owner_;
367-
366+ // Note: lock_ must be appear first (see b/313914119).
367+ // The lock and the associated member variables that are accessed when the
368+ // spinlock is acquired are placed together on the same cache line for cache
369+ // friendliness.
368370 absl::base_internal::SpinLock lock_;
369371
370- // Maximum size of the cache.
371- const int32_t max_capacity_;
372+ // All the following fields are accessed when holding lock_, so they should
373+ // be collocated with lock_ on the same cacheline. Align insert_hits_ to
374+ // ensure the following fields are on a separate cacheline.
375+
376+ // Lowest value of "slot_info_.used" since last call to TryPlunder. All
377+ // elements not used for a full cycle (2 seconds) are unlikely to get used
378+ // again.
379+ int low_water_mark_ ABSL_GUARDED_BY (lock_);
372380
373381 // insert_hits_ and remove_hits_ are logically guarded by lock_ for mutations
374382 // and use LossyAdd, but the thread annotations cannot indicate that we do not
375383 // need a lock for reads.
376384 StatsCounter insert_hits_;
377385 StatsCounter remove_hits_;
378- // For these we are deliberately fast-and-loose. Some increments may be lost.
379- StatsCounter insert_misses_;
380- StatsCounter remove_misses_;
381- MissCounts insert_object_misses_;
382- MissCounts remove_object_misses_;
383386
384387 // Number of currently used and available cached entries in slots_. This
385388 // variable is updated under a lock but can be read without one.
386389 // INVARIANT: [0 <= slot_info_.used <= slot_info.capacity <= max_cache_slots_]
387390 std::atomic<SizeInfo> slot_info_;
388391
389- // Lowest value of "slot_info_.used" since last call to TryPlunder. All
390- // elements not used for a full cycle (2 seconds) are unlikely to get used
391- // again.
392- int low_water_mark_ ABSL_GUARDED_BY (lock_);
393-
394392 // Pointer to array of free objects. Use GetSlot() to get pointers to
395393 // entries.
396394 void **slots_ ABSL_GUARDED_BY (lock_);
397395
398396 FreeList freelist_do_not_access_directly_;
397+
398+ Manager *const owner_;
399+
400+ // Maximum size of the cache.
401+ const int32_t max_capacity_;
402+
403+ // The following 4 *_misses_ counters
404+ // are frequently updated, so they should reside in a separate cacheline from
405+ // lock_.
406+
407+ // For these we are deliberately fast-and-loose. Some increments may be lost.
408+ StatsCounter insert_misses_;
409+ StatsCounter remove_misses_;
410+
411+ MissCounts insert_object_misses_;
412+ MissCounts remove_object_misses_;
399413} ABSL_CACHELINE_ALIGNED;
400414
401415template <typename Manager>
0 commit comments