LCOV - code coverage report
Current view: top level - src/base - memory_pool.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 413 418 98.8 %
Date: 2026-07-26 14:05:51 Functions: 42 43 97.7 %
Branches: 241 310 77.7 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./base/memory_pool.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <array>
      28                 :            : #include <atomic>
      29                 :            : #include <charconv>
      30                 :            : #include <cstddef>
      31                 :            : #include <cstdint>
      32                 :            : #include <exception>
      33                 :            : #include <mutex>
      34                 :            : #include <new>
      35                 :            : #include <string>
      36                 :            : #include <system_error>
      37                 :            : #include <utility>
      38                 :            : #include <vector>
      39                 :            : 
      40                 :            : #include "./base/logger.h"
      41                 :            : #include "./base/spin_lock.h"
      42                 :            : #include "./base/utils.h"
      43                 :            : 
      44                 :            : #define MEMORY_POOL_NEVER_DELETE 0
      45                 :            : 
      46                 :            : namespace vlink {
      47                 :            : 
      48                 :            : static constexpr int kMinMemoryLevel = 0;
      49                 :            : static constexpr int kMaxMemoryLevel = 9;
      50                 :            : static constexpr int kDefaultMemoryLevel = 3;
      51                 :            : static constexpr size_t kMaxTierCount = 20U;
      52                 :            : static constexpr size_t kMaxLevelCount = 10U;
      53                 :            : static constexpr size_t kInitialBlocksPerChunk = 1U;
      54                 :            : static constexpr size_t kInitialChunksReserve = 16U;
      55                 :            : static constexpr size_t kInitialChunkBytesTarget = 64U * 1024U;
      56                 :            : static constexpr size_t kTierShardCount = 8U;
      57                 :            : static constexpr size_t kDefaultBatchSize = 16U;
      58                 :            : static constexpr uint32_t kShardingContentionThreshold = 8U;
      59                 :            : 
      60                 :            : // clang-format off
      61                 :            : static constexpr MemoryPool::Tier kDefaultTierTable[kMaxLevelCount][kMaxTierCount] = {
      62                 :            :     // L0 ~ 0 MiB. (bypass; all entries are sentinels)
      63                 :            :     {
      64                 :            :         {32U, 0U},
      65                 :            :         {64U, 0U},
      66                 :            :         {128U, 0U},
      67                 :            :         {256U, 0U},
      68                 :            :         {512U, 0U},
      69                 :            :         {1U * 1024U, 0U},
      70                 :            :         {2U * 1024U, 0U},
      71                 :            :         {4U * 1024U, 0U},
      72                 :            :         {8U * 1024U, 0U},
      73                 :            :         {16U * 1024U, 0U},
      74                 :            :         {32U * 1024U, 0U},
      75                 :            :         {64U * 1024U, 0U},
      76                 :            :         {128U * 1024U, 0U},
      77                 :            :         {256U * 1024U, 0U},
      78                 :            :         {512U * 1024U, 0U},
      79                 :            :         {1U * 1024U * 1024U, 0U},
      80                 :            :         {4U * 1024U * 1024U, 0U},
      81                 :            :         {8U * 1024U * 1024U, 0U},
      82                 :            :         {16U * 1024U * 1024U, 0U},
      83                 :            :     },
      84                 :            :     // L1 ~ 4 MiB.
      85                 :            :     {
      86                 :            :         {32U, 8U * 1024U},
      87                 :            :         {64U, 4U * 1024U},
      88                 :            :         {128U, 2U * 1024U},
      89                 :            :         {256U, 1U * 1024U},
      90                 :            :         {512U, 512U},
      91                 :            :         {1U * 1024U, 256U},
      92                 :            :         {2U * 1024U, 128U},
      93                 :            :         {4U * 1024U, 64U},
      94                 :            :         {8U * 1024U, 32U},
      95                 :            :         {16U * 1024U, 16U},
      96                 :            :         {32U * 1024U, 8U},
      97                 :            :         {64U * 1024U, 4U},
      98                 :            :         {128U * 1024U, 2U},
      99                 :            :         {256U * 1024U, 1U},
     100                 :            :         {512U * 1024U, 1U},
     101                 :            :         {1U * 1024U * 1024U, 0U},
     102                 :            :         {4U * 1024U * 1024U, 0U},
     103                 :            :         {8U * 1024U * 1024U, 0U},
     104                 :            :         {16U * 1024U * 1024U, 0U},
     105                 :            :     },
     106                 :            :     // L2 ~ 8.5 MiB.
     107                 :            :     {
     108                 :            :         {32U, 16U * 1024U},
     109                 :            :         {64U, 8U * 1024U},
     110                 :            :         {128U, 4U * 1024U},
     111                 :            :         {256U, 2U * 1024U},
     112                 :            :         {512U, 1U * 1024U},
     113                 :            :         {1U * 1024U, 512U},
     114                 :            :         {2U * 1024U, 256U},
     115                 :            :         {4U * 1024U, 128U},
     116                 :            :         {8U * 1024U, 64U},
     117                 :            :         {16U * 1024U, 32U},
     118                 :            :         {32U * 1024U, 16U},
     119                 :            :         {64U * 1024U, 8U},
     120                 :            :         {128U * 1024U, 4U},
     121                 :            :         {256U * 1024U, 2U},
     122                 :            :         {512U * 1024U, 1U},
     123                 :            :         {1U * 1024U * 1024U, 1U},
     124                 :            :         {4U * 1024U * 1024U, 0U},
     125                 :            :         {8U * 1024U * 1024U, 0U},
     126                 :            :         {16U * 1024U * 1024U, 0U},
     127                 :            :     },
     128                 :            :     // L3 ~ 16 MiB. (Default)
     129                 :            :     {
     130                 :            :         {32U, 32U * 1024U},
     131                 :            :         {64U, 16U * 1024U},
     132                 :            :         {128U, 8U * 1024U},
     133                 :            :         {256U, 4U * 1024U},
     134                 :            :         {512U, 2U * 1024U},
     135                 :            :         {1U * 1024U, 1U * 1024U},
     136                 :            :         {2U * 1024U, 512U},
     137                 :            :         {4U * 1024U, 256U},
     138                 :            :         {8U * 1024U, 128U},
     139                 :            :         {16U * 1024U, 64U},
     140                 :            :         {32U * 1024U, 32U},
     141                 :            :         {64U * 1024U, 16U},
     142                 :            :         {128U * 1024U, 8U},
     143                 :            :         {256U * 1024U, 4U},
     144                 :            :         {512U * 1024U, 2U},
     145                 :            :         {1U * 1024U * 1024U, 1U},
     146                 :            :         {4U * 1024U * 1024U, 0U},
     147                 :            :         {8U * 1024U * 1024U, 0U},
     148                 :            :         {16U * 1024U * 1024U, 0U},
     149                 :            :     },
     150                 :            :     // L4 ~ 42 MiB.
     151                 :            :     {
     152                 :            :         {32U, 64U * 1024U},
     153                 :            :         {64U, 32U * 1024U},
     154                 :            :         {128U, 16U * 1024U},
     155                 :            :         {256U, 8U * 1024U},
     156                 :            :         {512U, 4U * 1024U},
     157                 :            :         {1U * 1024U, 2U * 1024U},
     158                 :            :         {2U * 1024U, 1U * 1024U},
     159                 :            :         {4U * 1024U, 512U},
     160                 :            :         {8U * 1024U, 256U},
     161                 :            :         {16U * 1024U, 128U},
     162                 :            :         {32U * 1024U, 64U},
     163                 :            :         {64U * 1024U, 32U},
     164                 :            :         {128U * 1024U, 16U},
     165                 :            :         {256U * 1024U, 8U},
     166                 :            :         {512U * 1024U, 4U},
     167                 :            :         {1U * 1024U * 1024U, 4U},
     168                 :            :         {4U * 1024U * 1024U, 2U},
     169                 :            :         {8U * 1024U * 1024U, 0U},
     170                 :            :         {16U * 1024U * 1024U, 0U},
     171                 :            :     },
     172                 :            :     // L5 ~ 92 MiB.
     173                 :            :     {
     174                 :            :         {32U, 128U * 1024U},
     175                 :            :         {64U, 64U * 1024U},
     176                 :            :         {128U, 32U * 1024U},
     177                 :            :         {256U, 16U * 1024U},
     178                 :            :         {512U, 8U * 1024U},
     179                 :            :         {1U * 1024U, 4U * 1024U},
     180                 :            :         {2U * 1024U, 2U * 1024U},
     181                 :            :         {4U * 1024U, 1U * 1024U},
     182                 :            :         {8U * 1024U, 512U},
     183                 :            :         {16U * 1024U, 256U},
     184                 :            :         {32U * 1024U, 128U},
     185                 :            :         {64U * 1024U, 64U},
     186                 :            :         {128U * 1024U, 32U},
     187                 :            :         {256U * 1024U, 16U},
     188                 :            :         {512U * 1024U, 8U},
     189                 :            :         {1U * 1024U * 1024U, 8U},
     190                 :            :         {4U * 1024U * 1024U, 4U},
     191                 :            :         {8U * 1024U * 1024U, 1U},
     192                 :            :         {16U * 1024U * 1024U, 0U},
     193                 :            :     },
     194                 :            :     // L6 ~ 200 MiB.
     195                 :            :     {
     196                 :            :         {32U, 256U * 1024U},
     197                 :            :         {64U, 128U * 1024U},
     198                 :            :         {128U, 64U * 1024U},
     199                 :            :         {256U, 32U * 1024U},
     200                 :            :         {512U, 16U * 1024U},
     201                 :            :         {1U * 1024U, 8U * 1024U},
     202                 :            :         {2U * 1024U, 4U * 1024U},
     203                 :            :         {4U * 1024U, 2U * 1024U},
     204                 :            :         {8U * 1024U, 1U * 1024U},
     205                 :            :         {16U * 1024U, 512U},
     206                 :            :         {32U * 1024U, 256U},
     207                 :            :         {64U * 1024U, 128U},
     208                 :            :         {128U * 1024U, 64U},
     209                 :            :         {256U * 1024U, 32U},
     210                 :            :         {512U * 1024U, 16U},
     211                 :            :         {1U * 1024U * 1024U, 16U},
     212                 :            :         {4U * 1024U * 1024U, 8U},
     213                 :            :         {8U * 1024U * 1024U, 2U},
     214                 :            :         {16U * 1024U * 1024U, 1U},
     215                 :            :     },
     216                 :            :     // L7 ~ 264 MiB.
     217                 :            :     {
     218                 :            :         {32U, 256U * 1024U},
     219                 :            :         {64U, 128U * 1024U},
     220                 :            :         {128U, 64U * 1024U},
     221                 :            :         {256U, 32U * 1024U},
     222                 :            :         {512U, 16U * 1024U},
     223                 :            :         {1U * 1024U, 8U * 1024U},
     224                 :            :         {2U * 1024U, 4U * 1024U},
     225                 :            :         {4U * 1024U, 2U * 1024U},
     226                 :            :         {8U * 1024U, 1U * 1024U},
     227                 :            :         {16U * 1024U, 512U},
     228                 :            :         {32U * 1024U, 256U},
     229                 :            :         {64U * 1024U, 128U},
     230                 :            :         {128U * 1024U, 64U},
     231                 :            :         {256U * 1024U, 32U},
     232                 :            :         {512U * 1024U, 16U},
     233                 :            :         {1U * 1024U * 1024U, 16U},
     234                 :            :         {4U * 1024U * 1024U, 16U},
     235                 :            :         {8U * 1024U * 1024U, 4U},
     236                 :            :         {16U * 1024U * 1024U, 2U},
     237                 :            :     },
     238                 :            :     // L8 ~ 528 MiB.
     239                 :            :     {
     240                 :            :         {32U, 512U * 1024U},
     241                 :            :         {64U, 256U * 1024U},
     242                 :            :         {128U, 128U * 1024U},
     243                 :            :         {256U, 64U * 1024U},
     244                 :            :         {512U, 32U * 1024U},
     245                 :            :         {1U * 1024U, 16U * 1024U},
     246                 :            :         {2U * 1024U, 8U * 1024U},
     247                 :            :         {4U * 1024U, 4U * 1024U},
     248                 :            :         {8U * 1024U, 2U * 1024U},
     249                 :            :         {16U * 1024U, 1U * 1024U},
     250                 :            :         {32U * 1024U, 512U},
     251                 :            :         {64U * 1024U, 256U},
     252                 :            :         {128U * 1024U, 128U},
     253                 :            :         {256U * 1024U, 64U},
     254                 :            :         {512U * 1024U, 32U},
     255                 :            :         {1U * 1024U * 1024U, 32U},
     256                 :            :         {4U * 1024U * 1024U, 32U},
     257                 :            :         {8U * 1024U * 1024U, 8U},
     258                 :            :         {16U * 1024U * 1024U, 4U},
     259                 :            :     },
     260                 :            :     // L9 ~ 656 MiB.
     261                 :            :     {
     262                 :            :         {32U, 512U * 1024U},
     263                 :            :         {64U, 256U * 1024U},
     264                 :            :         {128U, 128U * 1024U},
     265                 :            :         {256U, 64U * 1024U},
     266                 :            :         {512U, 32U * 1024U},
     267                 :            :         {1U * 1024U, 16U * 1024U},
     268                 :            :         {2U * 1024U, 8U * 1024U},
     269                 :            :         {4U * 1024U, 4U * 1024U},
     270                 :            :         {8U * 1024U, 2U * 1024U},
     271                 :            :         {16U * 1024U, 1U * 1024U},
     272                 :            :         {32U * 1024U, 512U},
     273                 :            :         {64U * 1024U, 256U},
     274                 :            :         {128U * 1024U, 128U},
     275                 :            :         {256U * 1024U, 64U},
     276                 :            :         {512U * 1024U, 32U},
     277                 :            :         {1U * 1024U * 1024U, 32U},
     278                 :            :         {4U * 1024U * 1024U, 32U},
     279                 :            :         {8U * 1024U * 1024U, 16U},
     280                 :            :         {16U * 1024U * 1024U, 8U},
     281                 :            :     }
     282                 :            : };
     283                 :            : // clang-format on
     284                 :            : 
     285                 :            : struct MemoryFreeNode final {
     286                 :            :   MemoryFreeNode* next{nullptr};
     287                 :            : };
     288                 :            : 
     289                 :            : struct MemoryChunk final {
     290                 :            :   void* ptr{nullptr};
     291                 :            :   size_t bytes{0};
     292                 :            : };
     293                 :            : 
     294                 :            : struct alignas(64) MemoryTierShard final {
     295                 :            :   SpinLock mtx;
     296                 :            :   MemoryFreeNode* free_list_head{nullptr};
     297                 :            :   std::atomic<uint64_t> hit_count{0};
     298                 :            :   std::atomic<uint64_t> deallocate_count{0};
     299                 :            : };
     300                 :            : 
     301                 :            : // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding)
     302                 :            : struct alignas(64) MemoryTierState final {
     303                 :            :   size_t max_size{0};
     304                 :            :   size_t block_size{0};
     305                 :            :   size_t blocks_per_chunk{0};
     306                 :            :   size_t next_chunk_blocks{0};
     307                 :            :   size_t initial_chunk_blocks{0};
     308                 :            :   size_t batch_size{kDefaultBatchSize};
     309                 :            : 
     310                 :            :   std::array<MemoryTierShard, kTierShardCount> shards;
     311                 :            :   std::vector<MemoryChunk> chunks;
     312                 :            :   std::mutex grow_mtx;
     313                 :            :   std::atomic<bool> sharded{false};
     314                 :            :   std::atomic<uint32_t> contention_count{0U};
     315                 :            : 
     316                 :            :   std::atomic<uint64_t> chunk_count{0};
     317                 :            :   std::atomic<uint64_t> upstream_alloc_count{0};
     318                 :            :   std::atomic<uint64_t> upstream_alloc_bytes{0};
     319                 :            : };
     320                 :            : 
     321                 :            : class MemoryTierShardLockGuard final {
     322                 :            :  public:
     323                 :       1346 :   explicit MemoryTierShardLockGuard(MemoryTierState& state) noexcept : state_(state) {
     324         [ +  + ]:      12114 :     for (auto& shard : state_.shards) {
     325                 :      10768 :       shard.mtx.lock();
     326                 :            :     }
     327                 :       1346 :   }
     328                 :            : 
     329                 :       1346 :   ~MemoryTierShardLockGuard() noexcept {
     330         [ +  + ]:      12114 :     for (size_t index = state_.shards.size(); index > 0U; --index) {
     331                 :      10768 :       state_.shards[index - 1U].mtx.unlock();
     332                 :            :     }
     333                 :       1346 :   }
     334                 :            : 
     335                 :            :  private:
     336                 :            :   MemoryTierState& state_;
     337                 :            : 
     338                 :            :   VLINK_DISALLOW_COPY_AND_ASSIGN(MemoryTierShardLockGuard)
     339                 :            : };
     340                 :            : 
     341                 :            : struct MemoryAllocCounters final {
     342                 :            :   std::atomic<uint64_t> count{0};
     343                 :            :   std::atomic<uint64_t> bytes{0};
     344                 :            : };
     345                 :            : 
     346   [ +  #  +  # ]:      90123 : static constexpr bool is_power_of_two(size_t x) noexcept { return x != 0 && ((x & (x - 1U)) == 0U); }
     347                 :            : 
     348                 :       3588 : static constexpr size_t round_up(size_t value, size_t alignment) noexcept {
     349                 :       3588 :   return (value + alignment - 1U) & ~(alignment - 1U);
     350                 :            : }
     351                 :            : 
     352                 :            : static constexpr bool default_tier_table_well_formed() noexcept {
     353                 :            :   // NOLINTNEXTLINE(modernize-loop-convert)
     354                 :            :   for (size_t level = 0; level < kMaxLevelCount; ++level) {
     355                 :            :     size_t prev_max_size = 0U;
     356                 :            : 
     357                 :            :     for (size_t t = 0; t < kMaxTierCount; ++t) {
     358                 :            :       const size_t max_size = kDefaultTierTable[level][t].max_size;
     359                 :            : 
     360                 :            :       if (max_size == 0U) {
     361                 :            :         break;
     362                 :            :       }
     363                 :            : 
     364                 :            :       if (max_size < sizeof(MemoryFreeNode)) {
     365                 :            :         return false;
     366                 :            :       }
     367                 :            : 
     368                 :            :       if (t > 0U && max_size <= prev_max_size) {
     369                 :            :         return false;
     370                 :            :       }
     371                 :            : 
     372                 :            :       prev_max_size = max_size;
     373                 :            :     }
     374                 :            :   }
     375                 :            : 
     376                 :            :   return true;
     377                 :            : }
     378                 :            : 
     379                 :            : static_assert(default_tier_table_well_formed(),
     380                 :            :               "MemoryPool: kDefaultTierTable contains a malformed row "
     381                 :            :               "(undersized tier or non-monotonic max_size)");
     382                 :            : 
     383                 :            : static std::atomic<size_t> next_tier_shard{0U};
     384                 :            : static thread_local size_t current_tier_shard_plus_one = 0U;
     385                 :            : 
     386                 :      64278 : static size_t current_tier_shard() noexcept {
     387         [ +  + ]:      64278 :   if VUNLIKELY (current_tier_shard_plus_one == 0U) {
     388                 :         12 :     current_tier_shard_plus_one = next_tier_shard.fetch_add(1U, std::memory_order_relaxed) % kTierShardCount + 1U;
     389                 :            :   }
     390                 :            : 
     391                 :      64278 :   return current_tier_shard_plus_one - 1U;
     392                 :            : }
     393                 :            : 
     394                 :      33833 : static MemoryFreeNode* pop_free_node(MemoryTierShard& shard) noexcept {
     395                 :      33833 :   SpinLockGuard lock(shard.mtx);
     396                 :            : 
     397         [ +  + ]:      34267 :   if (shard.free_list_head == nullptr) {
     398                 :       1537 :     return nullptr;
     399                 :            :   }
     400                 :            : 
     401                 :      32730 :   MemoryFreeNode* node = shard.free_list_head;
     402                 :      32730 :   shard.free_list_head = node->next;
     403                 :            : 
     404                 :      32730 :   return node;
     405                 :      34267 : }
     406                 :            : 
     407                 :       1535 : static MemoryFreeNode* steal_free_nodes(MemoryTierState& state, size_t target_index) noexcept {
     408                 :       1535 :   MemoryTierShard& target = state.shards[target_index];
     409                 :            : 
     410         [ +  + ]:      11488 :   for (size_t offset = 1U; offset < kTierShardCount; ++offset) {
     411                 :      10106 :     MemoryTierShard& source = state.shards[(target_index + offset) % kTierShardCount];
     412                 :      10106 :     MemoryFreeNode* first = nullptr;
     413                 :      10106 :     MemoryFreeNode* last = nullptr;
     414                 :            : 
     415                 :            :     {
     416                 :      10106 :       SpinLockGuard source_lock(source.mtx);
     417                 :            : 
     418         [ +  + ]:      10126 :       if (source.free_list_head == nullptr) {
     419                 :       9966 :         continue;
     420                 :            :       }
     421                 :            : 
     422                 :        160 :       first = source.free_list_head;
     423                 :        160 :       last = first;
     424                 :            : 
     425                 :        160 :       size_t count = 1U;
     426                 :            : 
     427   [ +  +  +  + ]:       1094 :       while (count < state.batch_size && last->next != nullptr) {
     428                 :        934 :         last = last->next;
     429                 :        934 :         ++count;
     430                 :            :       }
     431                 :            : 
     432                 :        160 :       source.free_list_head = last->next;
     433                 :        160 :       last->next = nullptr;
     434         [ +  + ]:      10126 :     }
     435                 :            : 
     436                 :        160 :     MemoryFreeNode* cached = first->next;
     437                 :        160 :     first->next = nullptr;
     438                 :            : 
     439         [ +  + ]:        160 :     if (cached != nullptr) {
     440                 :        108 :       SpinLockGuard target_lock(target.mtx);
     441                 :        108 :       last->next = target.free_list_head;
     442                 :        108 :       target.free_list_head = cached;
     443                 :        108 :     }
     444                 :            : 
     445                 :        160 :     return first;
     446                 :            :   }
     447                 :            : 
     448                 :       1382 :   return nullptr;
     449                 :            : }
     450                 :            : 
     451                 :      34014 : static MemoryFreeNode* try_allocate_from_shards(MemoryTierState& state, size_t shard_index) noexcept {
     452                 :      34014 :   MemoryFreeNode* node = pop_free_node(state.shards[shard_index]);
     453                 :            : 
     454         [ +  + ]:      33936 :   if VLIKELY (node != nullptr) {
     455                 :      32401 :     return node;
     456                 :            :   }
     457                 :            : 
     458                 :       1535 :   return steal_free_nodes(state, shard_index);
     459                 :            : }
     460                 :            : 
     461                 :            : // state.grow_mtx must be held.  When allocated is non-null, one node is removed from the new chunk.
     462                 :        674 : static bool grow_tier_chunk(MemoryTierState& state, size_t shard_index, MemoryFreeNode** allocated) noexcept {
     463                 :        674 :   size_t blocks = state.next_chunk_blocks;
     464                 :            : 
     465         [ -  + ]:        674 :   if VUNLIKELY (blocks > state.blocks_per_chunk) {
     466                 :            :     blocks = state.blocks_per_chunk;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     467                 :            :   }
     468                 :            : 
     469                 :        674 :   const size_t block_size = state.block_size;
     470                 :        674 :   const size_t chunk_bytes = block_size * blocks;
     471                 :            : 
     472         [ -  + ]:        674 :   if VUNLIKELY (chunk_bytes / block_size != blocks) {
     473                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     474                 :            :   }
     475                 :            : 
     476                 :        674 :   void* ptr = ::operator new(chunk_bytes, std::align_val_t{MemoryPool::kBlockAlignment}, std::nothrow);
     477                 :            : 
     478         [ -  + ]:        674 :   if VUNLIKELY (ptr == nullptr) {
     479                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     480                 :            :   }
     481                 :            : 
     482                 :        674 :   auto* base = static_cast<std::byte*>(ptr);
     483                 :        674 :   auto* local_tail = ::new (base + (blocks - 1U) * block_size) MemoryFreeNode{nullptr};
     484                 :        674 :   MemoryFreeNode* local_head = local_tail;
     485                 :            : 
     486         [ +  + ]:     285099 :   for (size_t i = blocks - 1U; i > 0; --i) {
     487                 :     284425 :     local_head = ::new (base + (i - 1U) * block_size) MemoryFreeNode{local_head};
     488                 :            :   }
     489                 :            : 
     490                 :            :   try {
     491         [ +  - ]:        674 :     state.chunks.push_back(MemoryChunk{ptr, chunk_bytes});
     492         [ -  - ]:          0 :   } catch (std::exception&) {
     493                 :            :     // LCOV_EXCL_START GCOVR_EXCL_START
     494                 :            :     ::operator delete(ptr, chunk_bytes, std::align_val_t{MemoryPool::kBlockAlignment});
     495                 :            :     return false;
     496                 :            :     // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     497                 :          0 :   }
     498                 :            : 
     499                 :        674 :   state.upstream_alloc_count.fetch_add(1, std::memory_order_relaxed);
     500                 :        674 :   state.upstream_alloc_bytes.fetch_add(chunk_bytes, std::memory_order_relaxed);
     501                 :        674 :   state.chunk_count.fetch_add(1, std::memory_order_relaxed);
     502                 :            : 
     503                 :            :   {
     504                 :        674 :     MemoryTierShard& shard = state.shards[shard_index];
     505                 :        674 :     SpinLockGuard lock(shard.mtx);
     506                 :            : 
     507                 :        674 :     local_tail->next = shard.free_list_head;
     508                 :        674 :     shard.free_list_head = local_head;
     509                 :            : 
     510         [ +  + ]:        674 :     if (allocated != nullptr) {
     511                 :        671 :       *allocated = shard.free_list_head;
     512                 :        671 :       shard.free_list_head = (*allocated)->next;
     513                 :        671 :       (*allocated)->next = nullptr;
     514                 :            :     }
     515                 :        674 :   }
     516                 :            : 
     517                 :        674 :   const size_t doubled = blocks * 2U;
     518         [ +  + ]:        674 :   const size_t target = (doubled < blocks || doubled > state.blocks_per_chunk)
     519         [ +  - ]:       1348 :                             ? state.blocks_per_chunk
     520                 :            :                             : doubled;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     521                 :            : 
     522         [ +  + ]:        674 :   if (target > state.next_chunk_blocks) {
     523                 :        386 :     state.next_chunk_blocks = target;
     524                 :            :   }
     525                 :            : 
     526                 :        674 :   return true;
     527                 :            : }
     528                 :            : 
     529                 :      46005 : static void* tier_allocate(MemoryTierState& state, size_t& shard_index) noexcept {
     530                 :      46005 :   shard_index = 0U;
     531                 :      46005 :   bool sharded = state.sharded.load(std::memory_order_relaxed);
     532                 :            : 
     533         [ +  + ]:      46737 :   if (!sharded) {
     534                 :      13684 :     MemoryTierShard& primary = state.shards.front();
     535                 :            : 
     536         [ +  + ]:      13674 :     if (primary.mtx.try_lock()) {
     537                 :      13646 :       MemoryFreeNode* node = primary.free_list_head;
     538                 :            : 
     539         [ +  + ]:      13646 :       if VLIKELY (node != nullptr) {
     540                 :      13205 :         primary.free_list_head = node->next;
     541                 :            :       }
     542                 :            : 
     543                 :      13646 :       primary.mtx.unlock();
     544                 :            : 
     545         [ +  + ]:      13650 :       if VLIKELY (node != nullptr) {
     546                 :      13209 :         return node;
     547                 :            :       }
     548                 :            :     } else {
     549                 :         28 :       const uint32_t contentions = state.contention_count.fetch_add(1U, std::memory_order_relaxed) + 1U;
     550                 :            : 
     551         [ +  + ]:         28 :       if (contentions >= kShardingContentionThreshold) {
     552                 :          3 :         state.sharded.store(true, std::memory_order_relaxed);
     553                 :          3 :         sharded = true;
     554                 :            :       }
     555                 :            :     }
     556                 :            :   }
     557                 :            : 
     558         [ +  + ]:      33522 :   if (sharded) {
     559                 :      32978 :     shard_index = current_tier_shard();
     560                 :            :   }
     561                 :            : 
     562         [ +  + ]:      33440 :   if (MemoryFreeNode* node = try_allocate_from_shards(state, shard_index)) {
     563                 :      32659 :     return node;
     564                 :            :   }
     565                 :            : 
     566                 :        515 :   std::lock_guard grow_lock(state.grow_mtx);
     567                 :            : 
     568         [ +  + ]:        708 :   if (MemoryFreeNode* node = try_allocate_from_shards(state, shard_index)) {
     569                 :         37 :     return node;
     570                 :            :   }
     571                 :            : 
     572                 :        671 :   MemoryFreeNode* node = nullptr;
     573                 :            : 
     574         [ -  + ]:        671 :   if VUNLIKELY (!grow_tier_chunk(state, shard_index, &node)) {
     575                 :          0 :     return nullptr;
     576                 :            :   }
     577                 :            : 
     578                 :        671 :   return node;
     579                 :        708 : }
     580                 :            : 
     581                 :      46088 : static size_t tier_deallocate(MemoryTierState& state, void* p) noexcept {
     582         [ +  + ]:      46088 :   if (!state.sharded.load(std::memory_order_relaxed)) {
     583                 :      13624 :     MemoryTierShard& primary = state.shards.front();
     584                 :            : 
     585         [ +  + ]:      13622 :     if (primary.mtx.try_lock()) {
     586                 :      13597 :       primary.free_list_head = ::new (p) MemoryFreeNode{primary.free_list_head};
     587                 :      13597 :       primary.mtx.unlock();
     588                 :            : 
     589                 :      13593 :       return 0U;
     590                 :            :     }
     591                 :            : 
     592                 :         31 :     const uint32_t contentions = state.contention_count.fetch_add(1U, std::memory_order_relaxed) + 1U;
     593                 :            : 
     594         [ +  + ]:         31 :     if (contentions < kShardingContentionThreshold) {
     595                 :         29 :       SpinLockGuard lock(primary.mtx);
     596                 :         29 :       primary.free_list_head = ::new (p) MemoryFreeNode{primary.free_list_head};
     597                 :            : 
     598                 :         29 :       return 0U;
     599                 :         29 :     }
     600                 :            : 
     601                 :          2 :     state.sharded.store(true, std::memory_order_relaxed);
     602                 :            :   }
     603                 :            : 
     604                 :      33063 :   const size_t shard_index = current_tier_shard();
     605                 :      32914 :   MemoryTierShard& shard = state.shards[shard_index];
     606                 :      32705 :   SpinLockGuard lock(shard.mtx);
     607                 :            : 
     608                 :      33138 :   auto* node = ::new (p) MemoryFreeNode{shard.free_list_head};
     609                 :            : 
     610                 :      33060 :   shard.free_list_head = node;
     611                 :            : 
     612                 :      33060 :   return shard_index;
     613                 :      33060 : }
     614                 :            : 
     615                 :          3 : static void prealloc_full_quota(MemoryTierState& state) noexcept {
     616                 :          3 :   std::lock_guard grow_lock(state.grow_mtx);
     617                 :            : 
     618                 :          3 :   state.next_chunk_blocks = state.blocks_per_chunk;
     619                 :          3 :   const bool ok = grow_tier_chunk(state, 0U, nullptr);
     620                 :            : 
     621         [ -  + ]:          3 :   if VUNLIKELY (!ok) {
     622                 :            :     state.next_chunk_blocks = state.initial_chunk_blocks;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     623                 :            :   }
     624                 :            : 
     625         [ -  + ]:          3 :   if VUNLIKELY (!ok) {
     626                 :          0 :     CLOG_W("MemoryPool: prealloc failed for tier (max_size=%zu, blocks_per_chunk=%zu); tier reverts to lazy growth.",
     627                 :            :            state.max_size, state.blocks_per_chunk);
     628                 :            :   }
     629                 :          3 : }
     630                 :            : 
     631                 :        249 : static bool validate_tiers_log(const std::vector<MemoryPool::Tier>& tiers) noexcept {
     632                 :            :   static constexpr size_t kMaxTierSize = SIZE_MAX - MemoryPool::kBlockAlignment + 1U;
     633                 :            : 
     634         [ +  + ]:        249 :   if VUNLIKELY (tiers.size() > kMaxTierCount) {
     635                 :          2 :     CLOG_E("MemoryPool: tier count %zu exceeds max %zu; falling back to default pyramid.", tiers.size(), kMaxTierCount);
     636                 :          1 :     return false;
     637                 :            :   }
     638                 :            : 
     639         [ +  + ]:       4423 :   for (size_t i = 0; i < tiers.size(); ++i) {
     640         [ +  + ]:       4180 :     if VUNLIKELY (tiers[i].max_size == 0) {
     641                 :          2 :       CLOG_E("MemoryPool: tier %zu has max_size == 0; falling back to default pyramid.", i);
     642                 :          5 :       return false;
     643                 :            :     }
     644                 :            : 
     645         [ +  + ]:       4179 :     if VUNLIKELY (tiers[i].max_size < sizeof(MemoryFreeNode)) {
     646                 :          2 :       CLOG_E(
     647                 :            :           "MemoryPool: tier %zu max_size (%zu) is below the minimum block size %zu; "
     648                 :            :           "falling back to default pyramid.",
     649                 :            :           i, tiers[i].max_size, sizeof(MemoryFreeNode));
     650                 :          1 :       return false;
     651                 :            :     }
     652                 :            : 
     653         [ +  + ]:       4178 :     if VUNLIKELY (tiers[i].max_size > kMaxTierSize) {
     654                 :          2 :       CLOG_E("MemoryPool: tier %zu max_size overflows after alignment rounding; falling back.", i);
     655                 :          1 :       return false;
     656                 :            :     }
     657                 :            : 
     658   [ +  +  +  +  :       4177 :     if VUNLIKELY (i > 0 && tiers[i].max_size <= tiers[i - 1].max_size) {
                   +  + ]
     659                 :          4 :       CLOG_E("MemoryPool: tier %zu max_size is not strictly increasing; falling back to default pyramid.", i);
     660                 :          2 :       return false;
     661                 :            :     }
     662                 :            :   }
     663                 :            : 
     664                 :        243 :   return true;
     665                 :            : }
     666                 :            : 
     667                 :        224 : static MemoryPool::Config create_memory_config(int level, bool prealloc) {
     668   [ +  -  +  +  :        224 :   if VUNLIKELY (level < kMinMemoryLevel || level > kMaxMemoryLevel) {
                   +  + ]
     669   [ +  -  +  - ]:          2 :     CLOG_W("MemoryPool: level %d out of range [%d, %d], clamped.", level, kMinMemoryLevel, kMaxMemoryLevel);
     670         [ -  + ]:          1 :     level = (level < kMinMemoryLevel) ? kMinMemoryLevel : kMaxMemoryLevel;
     671                 :            :   }
     672                 :            : 
     673                 :        224 :   const auto row_index = static_cast<size_t>(level - kMinMemoryLevel);
     674                 :        224 :   const auto& row = kDefaultTierTable[row_index];
     675                 :            : 
     676                 :        224 :   MemoryPool::Config config;
     677                 :        224 :   config.prealloc = prealloc;
     678         [ +  - ]:        224 :   config.tiers.reserve(kMaxTierCount);
     679                 :            : 
     680   [ +  -  +  + ]:       4480 :   for (size_t i = 0; i < kMaxTierCount && row[i].max_size != 0; ++i) {
     681         [ +  - ]:       4256 :     config.tiers.emplace_back(row[i]);
     682                 :            :   }
     683                 :            : 
     684                 :        224 :   return config;
     685                 :            : }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     686                 :            : 
     687                 :            : struct MemoryPool::Impl final {  // NOLINT(clang-analyzer-optin.performance.Padding)
     688                 :            :   alignas(64) size_t dispatch_max_sizes[kMaxTierCount]{};
     689                 :            :   MemoryTierState* dispatch_states[kMaxTierCount]{};
     690                 :            :   size_t dispatch_count{0};
     691                 :            : 
     692                 :            :   MemoryTierState* tier_states[kMaxTierCount]{};
     693                 :            :   size_t tier_count{0};
     694                 :            :   std::vector<std::unique_ptr<MemoryTierState>> owned_states;
     695                 :            :   MemoryAllocCounters oversized_alloc;
     696                 :            : 
     697                 :            :   std::atomic<uint64_t> oversized_dealloc_count{0};
     698                 :            : };
     699                 :            : 
     700         [ +  - ]:          4 : MemoryPool::MemoryPool() : MemoryPool(Config{}) {}
     701                 :            : 
     702         [ +  - ]:         16 : MemoryPool::MemoryPool(int level, bool prealloc) : MemoryPool(create_memory_config(level, prealloc)) {}
     703                 :            : 
     704                 :        255 : MemoryPool::MemoryPool(const Config& config) : impl_(std::make_unique<Impl>()) {
     705         [ +  + ]:        255 :   if (config.tiers.empty()) {
     706                 :          6 :     impl_->tier_count = 0;
     707                 :          6 :     return;
     708                 :            :   }
     709                 :            : 
     710                 :        249 :   std::vector<Tier> fallback;
     711                 :        249 :   const bool use_caller = validate_tiers_log(config.tiers);
     712                 :            : 
     713         [ +  + ]:        249 :   if VUNLIKELY (!use_caller) {
     714                 :          6 :     const auto& row = kDefaultTierTable[kDefaultMemoryLevel - kMinMemoryLevel];
     715         [ +  - ]:          6 :     fallback.assign(row, row + kMaxTierCount);
     716                 :            :   }
     717                 :            : 
     718         [ +  + ]:        249 :   const std::vector<Tier>& source = use_caller ? config.tiers : fallback;
     719         [ +  + ]:        249 :   const size_t batch_size = config.batch_size == 0U ? kDefaultBatchSize : config.batch_size;
     720                 :            : 
     721         [ +  + ]:        249 :   if VUNLIKELY (config.batch_size == 0U) {
     722   [ +  -  +  - ]:          2 :     CLOG_W("MemoryPool: batch_size is 0; fallback to %zu.", kDefaultBatchSize);
     723                 :            :   }
     724                 :            : 
     725         [ +  - ]:        249 :   impl_->owned_states.reserve(source.size());
     726                 :            : 
     727                 :        249 :   size_t live = 0;
     728                 :        249 :   size_t dispatch = 0;
     729                 :            : 
     730         [ +  + ]:       4542 :   for (const auto& cfg : source) {
     731         [ +  + ]:       4293 :     if VUNLIKELY (cfg.max_size == 0U) {
     732                 :        705 :       continue;
     733                 :            :     }
     734                 :            : 
     735                 :       4287 :     impl_->dispatch_max_sizes[dispatch] = cfg.max_size;
     736                 :            : 
     737         [ +  + ]:       4287 :     if VUNLIKELY (cfg.blocks_per_chunk == 0U) {
     738                 :        699 :       impl_->dispatch_states[dispatch] = nullptr;
     739                 :        699 :       ++dispatch;
     740                 :        699 :       continue;
     741                 :            :     }
     742                 :            : 
     743         [ +  - ]:       3588 :     auto state = std::make_unique<MemoryTierState>();
     744                 :       3588 :     state->max_size = cfg.max_size;
     745                 :       3588 :     state->blocks_per_chunk = cfg.blocks_per_chunk;
     746                 :       3588 :     state->batch_size = batch_size;
     747         [ +  - ]:       3588 :     state->chunks.reserve(kInitialChunksReserve);
     748                 :       3588 :     state->block_size = round_up(cfg.max_size, kBlockAlignment);
     749                 :            : 
     750         [ +  - ]:       3588 :     size_t initial = (state->block_size > 0U) ? (kInitialChunkBytesTarget / state->block_size) : kInitialBlocksPerChunk;
     751                 :            : 
     752         [ +  + ]:       3588 :     if (initial < kInitialBlocksPerChunk) {
     753                 :        887 :       initial = kInitialBlocksPerChunk;
     754                 :            :     }
     755                 :            : 
     756         [ +  + ]:       3588 :     if (initial > state->blocks_per_chunk) {
     757                 :         49 :       initial = state->blocks_per_chunk;
     758                 :            :     }
     759                 :            : 
     760                 :       3588 :     state->initial_chunk_blocks = initial;
     761                 :       3588 :     state->next_chunk_blocks = initial;
     762                 :            : 
     763                 :       3588 :     impl_->tier_states[live] = state.get();
     764                 :       3588 :     impl_->dispatch_states[dispatch] = state.get();
     765         [ +  - ]:       3588 :     impl_->owned_states.emplace_back(std::move(state));
     766                 :            : 
     767                 :       3588 :     ++live;
     768                 :       3588 :     ++dispatch;
     769                 :       3588 :   }
     770                 :            : 
     771                 :        249 :   impl_->dispatch_count = dispatch;
     772                 :        249 :   impl_->tier_count = live;
     773                 :            : 
     774         [ +  + ]:        249 :   if (config.prealloc) {
     775         [ +  + ]:          5 :     for (auto& state : impl_->owned_states) {
     776                 :          3 :       prealloc_full_quota(*state);
     777                 :            :     }
     778                 :            :   }
     779                 :        249 : }
     780                 :            : 
     781                 :        255 : MemoryPool::~MemoryPool() {
     782         [ +  + ]:       3843 :   for (auto& state : impl_->owned_states) {
     783         [ +  + ]:       4008 :     for (const MemoryChunk& chunk : state->chunks) {
     784                 :        420 :       ::operator delete(chunk.ptr, chunk.bytes, std::align_val_t{kBlockAlignment});
     785                 :            :     }
     786                 :            : 
     787                 :       3588 :     state->chunks.clear();
     788                 :            : 
     789         [ +  + ]:      32292 :     for (auto& shard : state->shards) {
     790                 :      28704 :       shard.free_list_head = nullptr;
     791                 :            :     }
     792                 :            :   }
     793                 :        255 : }
     794                 :            : 
     795                 :      46038 : void* MemoryPool::allocate(size_t bytes, size_t alignment) noexcept {
     796         [ +  + ]:      46038 :   if VUNLIKELY (!is_power_of_two(alignment)) {
     797                 :          2 :     CLOG_E("MemoryPool::allocate: alignment %zu is not a power of two; returning nullptr.", alignment);
     798                 :        757 :     return nullptr;
     799                 :            :   }
     800                 :            : 
     801                 :      45950 :   const size_t idx = find_tier(bytes);
     802                 :            : 
     803   [ +  #  +  +  :      46459 :   if VUNLIKELY (idx == kMaxTierCount || alignment > kBlockAlignment || impl_->dispatch_states[idx] == nullptr) {
          +  #  +  +  +  
                      + ]
     804                 :         85 :     void* p = ::operator new(bytes, std::align_val_t{alignment}, std::nothrow);
     805                 :            : 
     806         [ -  + ]:         85 :     if VUNLIKELY (p == nullptr) {
     807                 :            :       return nullptr;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     808                 :            :     }
     809                 :            : 
     810                 :         85 :     impl_->oversized_alloc.count.fetch_add(1, std::memory_order_relaxed);
     811                 :         85 :     impl_->oversized_alloc.bytes.fetch_add(bytes, std::memory_order_relaxed);
     812                 :            : 
     813                 :         85 :     return p;
     814                 :            :   }
     815                 :            : 
     816                 :      45849 :   MemoryTierState& state = *impl_->dispatch_states[idx];
     817                 :      46168 :   size_t shard_index = 0U;
     818                 :      46168 :   void* block = tier_allocate(state, shard_index);
     819                 :            : 
     820         [ -  + ]:      46375 :   if VUNLIKELY (block == nullptr) {
     821                 :            :     return nullptr;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     822                 :            :   }
     823                 :            : 
     824                 :      46375 :   state.shards[shard_index].hit_count.fetch_add(1, std::memory_order_relaxed);
     825                 :            : 
     826                 :      46217 :   return block;
     827                 :            : }
     828                 :            : 
     829                 :      45971 : void MemoryPool::deallocate(void* p, size_t bytes, size_t alignment) noexcept {
     830         [ #  + ]:      45971 :   if VUNLIKELY (!is_power_of_two(alignment)) {
     831                 :          0 :     CLOG_E("MemoryPool::deallocate: alignment %zu is not a power of two; leaking %p.", alignment, p);
     832                 :          1 :     return;
     833                 :            :   }
     834                 :            : 
     835         [ +  + ]:      46062 :   if VUNLIKELY (p == nullptr) {
     836                 :          2 :     return;
     837                 :            :   }
     838                 :            : 
     839                 :      46060 :   const size_t idx = find_tier(bytes);
     840                 :            : 
     841   [ +  #  +  +  :      46471 :   if VUNLIKELY (idx == kMaxTierCount || alignment > kBlockAlignment || impl_->dispatch_states[idx] == nullptr) {
          +  +  #  +  +  
                      + ]
     842                 :         84 :     ::operator delete(p, bytes, std::align_val_t{alignment});
     843                 :         84 :     impl_->oversized_dealloc_count.fetch_add(1, std::memory_order_relaxed);
     844                 :            : 
     845                 :         84 :     return;
     846                 :            :   }
     847                 :            : 
     848                 :      46083 :   MemoryTierState& state = *impl_->dispatch_states[idx];
     849                 :      46185 :   const size_t shard_index = tier_deallocate(state, p);
     850                 :      46084 :   state.shards[shard_index].deallocate_count.fetch_add(1, std::memory_order_relaxed);
     851                 :            : }
     852                 :            : 
     853                 :         17 : size_t MemoryPool::get_tier_count() const noexcept { return impl_->tier_count; }
     854                 :            : 
     855                 :         42 : std::vector<MemoryPool::TierStats> MemoryPool::get_stats() const noexcept {
     856                 :         42 :   const size_t count = impl_->tier_count;
     857                 :            : 
     858                 :         42 :   std::vector<TierStats> result;
     859                 :            : 
     860                 :            :   try {
     861         [ +  - ]:         42 :     result.reserve(count);
     862                 :            :   } catch (...) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     863                 :            :     return {};     // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     864                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     865                 :            : 
     866         [ +  + ]:        340 :   for (size_t i = 0; i < count; ++i) {
     867                 :        298 :     const MemoryTierState& state = *impl_->tier_states[i];
     868                 :        298 :     uint64_t hits = 0U;
     869                 :        298 :     uint64_t deallocs = 0U;
     870                 :            : 
     871         [ +  + ]:       2682 :     for (const auto& shard : state.shards) {
     872                 :       2384 :       hits += shard.hit_count.load(std::memory_order_relaxed);
     873                 :       4768 :       deallocs += shard.deallocate_count.load(std::memory_order_relaxed);
     874                 :            :     }
     875                 :            : 
     876                 :        298 :     TierStats item;
     877                 :        298 :     item.max_size = state.max_size;
     878                 :        298 :     item.blocks_per_chunk = state.blocks_per_chunk;
     879                 :        298 :     item.block_size = state.block_size;
     880                 :        298 :     item.hit_count = hits;
     881                 :        298 :     item.deallocate_count = deallocs;
     882         [ +  - ]:        298 :     item.in_use_blocks = (hits >= deallocs) ? (hits - deallocs) : 0U;
     883                 :        298 :     item.upstream_alloc_count = state.upstream_alloc_count.load(std::memory_order_relaxed);
     884                 :        298 :     item.upstream_alloc_bytes = state.upstream_alloc_bytes.load(std::memory_order_relaxed);
     885                 :        298 :     item.chunk_count = state.chunk_count.load(std::memory_order_relaxed);
     886                 :            : 
     887                 :        298 :     result.emplace_back(item);
     888                 :            :   }
     889                 :            : 
     890                 :         42 :   return result;
     891                 :         42 : }
     892                 :            : 
     893                 :         34 : MemoryPool::OversizedStats MemoryPool::get_oversized_stats() const noexcept {
     894                 :         34 :   OversizedStats result;
     895                 :            : 
     896                 :         34 :   result.alloc_count = impl_->oversized_alloc.count.load(std::memory_order_relaxed);
     897                 :         34 :   result.alloc_bytes = impl_->oversized_alloc.bytes.load(std::memory_order_relaxed);
     898                 :         34 :   result.dealloc_count = impl_->oversized_dealloc_count.load(std::memory_order_relaxed);
     899                 :            : 
     900                 :         34 :   return result;
     901                 :            : }
     902                 :            : 
     903                 :          1 : void MemoryPool::reset_stats() noexcept {
     904                 :          1 :   const size_t count = impl_->tier_count;
     905                 :            : 
     906         [ +  + ]:          2 :   for (size_t i = 0; i < count; ++i) {
     907                 :          1 :     MemoryTierState& state = *impl_->tier_states[i];
     908                 :            : 
     909         [ +  + ]:          9 :     for (auto& shard : state.shards) {
     910                 :          8 :       shard.hit_count.store(0, std::memory_order_relaxed);
     911                 :          8 :       shard.deallocate_count.store(0, std::memory_order_relaxed);
     912                 :            :     }
     913                 :            :   }
     914                 :            : 
     915                 :          1 :   impl_->oversized_alloc.count.store(0, std::memory_order_relaxed);
     916                 :          1 :   impl_->oversized_alloc.bytes.store(0, std::memory_order_relaxed);
     917                 :          1 :   impl_->oversized_dealloc_count.store(0, std::memory_order_relaxed);
     918                 :          1 : }
     919                 :            : 
     920                 :       1314 : void MemoryPool::clear() noexcept {
     921                 :            :   static constexpr size_t kStackSlots = 64U;
     922                 :            : 
     923         [ +  + ]:       2660 :   for (auto& state : impl_->owned_states) {
     924                 :       1346 :     std::unique_lock grow_lock(state->grow_mtx);
     925                 :            : 
     926                 :       1346 :     size_t stack_free_counts[kStackSlots] = {};
     927                 :       1346 :     MemoryChunk stack_to_delete[kStackSlots];
     928                 :            : 
     929                 :       1346 :     std::vector<size_t> heap_free_counts;
     930                 :       1346 :     std::vector<MemoryChunk> heap_to_delete;
     931                 :            : 
     932                 :       1346 :     const size_t chunks_hint = state->chunk_count.load(std::memory_order_relaxed);
     933                 :            : 
     934         [ -  + ]:       1346 :     if VUNLIKELY (chunks_hint > kStackSlots) {
     935                 :            :       try {
     936                 :            :         // LCOV_EXCL_START GCOVR_EXCL_START
     937                 :            :         heap_free_counts.reserve(chunks_hint);
     938                 :            :         heap_to_delete.reserve(chunks_hint);
     939                 :            :       } catch (std::exception&) {
     940                 :            :       }
     941                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     942                 :            :     }
     943                 :            : 
     944                 :       1346 :     MemoryChunk* to_delete = nullptr;
     945                 :       1346 :     size_t to_delete_count = 0U;
     946                 :            : 
     947                 :            :     {
     948                 :       1346 :       MemoryTierShardLockGuard shard_locks(*state);
     949                 :            : 
     950                 :       1346 :       const size_t chunk_count = state->chunks.size();
     951                 :            : 
     952         [ +  + ]:       1346 :       if VUNLIKELY (chunk_count == 0U) {
     953                 :        466 :         continue;
     954                 :            :       }
     955                 :            : 
     956                 :        880 :       size_t* free_counts = stack_free_counts;
     957                 :            : 
     958                 :        880 :       const bool spill_to_heap = (chunk_count > kStackSlots);
     959                 :            : 
     960         [ -  + ]:        880 :       if VUNLIKELY (spill_to_heap) {
     961                 :            :         try {
     962                 :            :           // LCOV_EXCL_START GCOVR_EXCL_START
     963                 :            :           heap_free_counts.assign(chunk_count, 0U);
     964                 :            :         } catch (std::exception&) {
     965                 :            :           continue;
     966                 :            :         }
     967                 :            : 
     968                 :            :         free_counts = heap_free_counts.data();
     969                 :            :         // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     970                 :            :       }
     971                 :            : 
     972                 :        880 :       const size_t block_size = state->block_size;
     973                 :            : 
     974                 :        880 :       std::sort(state->chunks.begin(), state->chunks.end(), [](const MemoryChunk& a, const MemoryChunk& b) noexcept {
     975                 :          3 :         return reinterpret_cast<std::uintptr_t>(a.ptr) < reinterpret_cast<std::uintptr_t>(b.ptr);
     976                 :            :       });
     977                 :            : 
     978                 :     674074 :       const auto find_chunk_idx = [&chunk_count, &state](std::uintptr_t addr) noexcept -> size_t {
     979                 :     224682 :         size_t lo = 0;
     980                 :     224682 :         size_t hi = chunk_count;
     981                 :            : 
     982         [ +  - ]:     224696 :         while (lo < hi) {
     983                 :     224696 :           const size_t mid = lo + (hi - lo) / 2U;
     984                 :     224696 :           const auto cs = reinterpret_cast<std::uintptr_t>(state->chunks[mid].ptr);
     985                 :     224696 :           const auto ce = cs + state->chunks[mid].bytes;
     986                 :            : 
     987         [ +  + ]:     224696 :           if (addr < cs) {
     988                 :         14 :             hi = mid;
     989         [ -  + ]:     224682 :           } else if (addr >= ce) {
     990                 :            :             lo = mid + 1U;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     991                 :            :           } else {
     992                 :     224682 :             return mid;
     993                 :            :           }
     994                 :            :         }
     995                 :            : 
     996                 :            :         return SIZE_MAX;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     997                 :        880 :       };
     998                 :            : 
     999         [ +  + ]:       7920 :       for (const auto& shard : state->shards) {
    1000         [ +  + ]:     119381 :         for (MemoryFreeNode* node = shard.free_list_head; node != nullptr; node = node->next) {
    1001                 :     112341 :           const size_t idx = find_chunk_idx(reinterpret_cast<std::uintptr_t>(node));
    1002                 :            : 
    1003         [ +  - ]:     112341 :           if VLIKELY (idx != SIZE_MAX) {
    1004                 :     112341 :             ++free_counts[idx];
    1005                 :            :           }
    1006                 :            :         }
    1007                 :            :       }
    1008                 :            : 
    1009         [ +  + ]:       7920 :       for (auto& shard : state->shards) {
    1010                 :       7040 :         MemoryFreeNode* new_head = nullptr;
    1011                 :       7040 :         MemoryFreeNode* current = shard.free_list_head;
    1012                 :            : 
    1013         [ +  + ]:     119381 :         while (current != nullptr) {
    1014                 :     112341 :           MemoryFreeNode* next = current->next;
    1015                 :     112341 :           const size_t idx = find_chunk_idx(reinterpret_cast<std::uintptr_t>(current));
    1016                 :            : 
    1017                 :     112341 :           bool keep = false;
    1018                 :            : 
    1019         [ +  - ]:     112341 :           if VLIKELY (idx != SIZE_MAX) {
    1020                 :     112341 :             const size_t total_blocks = state->chunks[idx].bytes / block_size;
    1021                 :     112341 :             keep = (free_counts[idx] != total_blocks);
    1022                 :            :           }
    1023                 :            : 
    1024         [ +  + ]:     112341 :           if (keep) {
    1025                 :      79172 :             current->next = new_head;
    1026                 :      79172 :             new_head = current;
    1027                 :            :           }
    1028                 :            : 
    1029                 :     112341 :           current = next;
    1030                 :            :         }
    1031                 :            : 
    1032                 :       7040 :         shard.free_list_head = new_head;
    1033                 :            :       }
    1034                 :            : 
    1035                 :        880 :       size_t released = 0U;
    1036                 :        880 :       size_t write = 0U;
    1037                 :            : 
    1038         [ +  + ]:       1762 :       for (size_t read = 0U; read < chunk_count; ++read) {
    1039                 :        882 :         const size_t total_blocks = state->chunks[read].bytes / block_size;
    1040                 :            : 
    1041         [ +  + ]:        882 :         if (free_counts[read] == total_blocks) {
    1042         [ +  - ]:        254 :           if VLIKELY (!spill_to_heap) {
    1043                 :        254 :             stack_to_delete[released] = state->chunks[read];
    1044                 :            :           } else {
    1045                 :            :             try {
    1046                 :            :               // LCOV_EXCL_START GCOVR_EXCL_START
    1047                 :            :               heap_to_delete.push_back(state->chunks[read]);
    1048                 :            :             } catch (std::exception&) {
    1049                 :            :               ::operator delete(state->chunks[read].ptr, state->chunks[read].bytes, std::align_val_t{kBlockAlignment});
    1050                 :            :             }
    1051                 :            :             // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1052                 :            :           }
    1053                 :            : 
    1054                 :        254 :           ++released;
    1055                 :            :         } else {
    1056         [ -  + ]:        628 :           if (read != write) {
    1057                 :            :             state->chunks[write] = state->chunks[read];  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1058                 :            :           }
    1059                 :            : 
    1060                 :        628 :           ++write;
    1061                 :            :         }
    1062                 :            :       }
    1063                 :            : 
    1064         [ +  + ]:        880 :       if VLIKELY (released > 0U) {
    1065                 :        253 :         state->chunks.resize(write);
    1066                 :        253 :         state->chunk_count.fetch_sub(released, std::memory_order_relaxed);
    1067                 :            :       }
    1068                 :            : 
    1069         [ +  - ]:        880 :       if VLIKELY (!spill_to_heap) {
    1070                 :        880 :         to_delete = stack_to_delete;
    1071                 :        880 :         to_delete_count = released;
    1072                 :            :       } else {
    1073                 :            :         to_delete = heap_to_delete.data();        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1074                 :            :         to_delete_count = heap_to_delete.size();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
    1075                 :            :       }
    1076         [ +  + ]:       1346 :     }
    1077                 :            : 
    1078                 :        880 :     grow_lock.unlock();
    1079                 :            : 
    1080         [ +  + ]:       1134 :     for (size_t i = 0; i < to_delete_count; ++i) {
    1081                 :        254 :       ::operator delete(to_delete[i].ptr, to_delete[i].bytes, std::align_val_t{kBlockAlignment});
    1082                 :            :     }
    1083   [ +  +  +  +  :       2278 :   }
                   +  + ]
    1084                 :       1314 : }
    1085                 :            : 
    1086                 :          2 : void MemoryPool::trim() noexcept { clear(); }
    1087                 :            : 
    1088                 :        208 : MemoryPool::Config MemoryPool::get_default_config() {
    1089                 :        200 :   static int level = []() noexcept {
    1090                 :        400 :     const std::string env_value = Utils::get_env("VLINK_MEMORY_LEVEL", "3");
    1091                 :            : 
    1092                 :        200 :     int parsed = kDefaultMemoryLevel;
    1093                 :            : 
    1094                 :        200 :     const char* first = env_value.data();
    1095                 :        200 :     const char* last = first + env_value.size();
    1096                 :            : 
    1097                 :        200 :     auto [ptr, ec] = std::from_chars(first, last, parsed);
    1098                 :            : 
    1099   [ +  -  -  +  :        200 :     if VUNLIKELY (ec != std::errc() || ptr != last) {
                   -  + ]
    1100                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
    1101                 :            :       CLOG_W("MemoryPool: VLINK_MEMORY_LEVEL=\"%s\" is not a valid integer, fallback to %d.", env_value.c_str(),
    1102                 :            :              kDefaultMemoryLevel);
    1103                 :            : 
    1104                 :            :       return kDefaultMemoryLevel;
    1105                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1106                 :            :     }
    1107                 :            : 
    1108   [ +  -  -  +  :        200 :     if VUNLIKELY (parsed < kMinMemoryLevel || parsed > kMaxMemoryLevel) {
                   -  + ]
    1109                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
    1110                 :            :       CLOG_W("MemoryPool: VLINK_MEMORY_LEVEL=%d out of range [%d, %d], clamped.", parsed, kMinMemoryLevel,
    1111                 :            :              kMaxMemoryLevel);
    1112                 :            : 
    1113                 :            :       return parsed < kMinMemoryLevel ? kMinMemoryLevel : kMaxMemoryLevel;
    1114                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1115                 :            :     }
    1116                 :            : 
    1117                 :        200 :     return parsed;
    1118   [ +  +  +  - ]:        408 :   }();
    1119                 :            : 
    1120   [ +  +  +  -  :        208 :   static bool prealloc_env = (Utils::get_env("VLINK_MEMORY_PREALLOC") == "1");
          +  -  +  -  -  
                      - ]
    1121                 :            : 
    1122                 :        200 :   static size_t batch_size = []() noexcept {
    1123                 :        400 :     const std::string env_value = Utils::get_env("VLINK_MEMORY_BATCH_SIZE", "16");
    1124                 :        200 :     size_t parsed = kDefaultBatchSize;
    1125                 :            : 
    1126                 :        200 :     const char* first = env_value.data();
    1127                 :        200 :     const char* last = first + env_value.size();
    1128                 :        200 :     auto [ptr, ec] = std::from_chars(first, last, parsed);
    1129                 :            : 
    1130   [ +  +  -  +  :        200 :     if VUNLIKELY (ec != std::errc() || ptr != last || parsed == 0U) {
          +  +  +  +  +  
                      + ]
    1131                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
    1132                 :            :       CLOG_W("MemoryPool: VLINK_MEMORY_BATCH_SIZE=\"%s\" is not a positive integer, fallback to %zu.",
    1133                 :            :              env_value.c_str(), kDefaultBatchSize);
    1134                 :            :       return kDefaultBatchSize;
    1135                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
    1136                 :            :     }
    1137                 :            : 
    1138                 :        198 :     return parsed;
    1139   [ +  +  +  - ]:        408 :   }();
    1140                 :            : 
    1141                 :        208 :   Config config = create_memory_config(level, prealloc_env);
    1142                 :        208 :   config.batch_size = batch_size;
    1143                 :            : 
    1144                 :        208 :   return config;
    1145                 :            : }
    1146                 :            : 
    1147                 :      11314 : MemoryPool& MemoryPool::global_instance(bool use_env_level) {
    1148                 :            : #if MEMORY_POOL_NEVER_DELETE
    1149                 :            :   alignas(MemoryPool) static char buf[sizeof(MemoryPool)];
    1150                 :            : 
    1151                 :            :   static auto* instance =
    1152                 :            :       new (buf) MemoryPool(use_env_level ? get_default_config() : create_memory_config(kDefaultMemoryLevel, false));
    1153                 :            : 
    1154                 :            :   return *instance;
    1155                 :            : #else
    1156   [ +  +  +  -  :      11314 :   static MemoryPool instance(use_env_level ? get_default_config() : create_memory_config(kDefaultMemoryLevel, false));
          +  -  +  -  -  
             -  +  -  -  
                      - ]
    1157                 :            : 
    1158                 :      11313 :   return instance;
    1159                 :            : #endif
    1160                 :            : }
    1161                 :            : 
    1162                 :      90426 : size_t MemoryPool::find_tier(size_t bytes) const noexcept {
    1163                 :      90426 :   const size_t count = impl_->dispatch_count;
    1164                 :      89081 :   const size_t* const sizes = impl_->dispatch_max_sizes;
    1165                 :            : 
    1166         [ +  + ]:     353144 :   for (size_t i = 0; i < count; ++i) {
    1167         [ +  + ]:     353130 :     if (bytes <= sizes[i]) {
    1168                 :      91268 :       return i;
    1169                 :            :     }
    1170                 :            :   }
    1171                 :            : 
    1172                 :         14 :   return kMaxTierCount;
    1173                 :            : }
    1174                 :            : 
    1175                 :            : }  // namespace vlink

Generated by: LCOV version 1.14