| 272 | } |
| 273 | |
| 274 | Status BufferPool::BufferAllocator::AllocateInternal( |
| 275 | BufferPool::Client* client, int64_t len, BufferHandle* buffer) { |
| 276 | DCHECK(!buffer->is_open()); |
| 277 | DCHECK_GE(len, min_buffer_len_); |
| 278 | DCHECK(BitUtil::IsPowerOf2(len)) << len; |
| 279 | |
| 280 | if (UNLIKELY(len > MAX_BUFFER_BYTES)) { |
| 281 | return Status(Substitute( |
| 282 | "Tried to allocate buffer of $0 bytes > max of $1 bytes", len, MAX_BUFFER_BYTES)); |
| 283 | } |
| 284 | if (UNLIKELY(len > system_bytes_limit_)) { |
| 285 | return Status(Substitute("Tried to allocate buffer of $0 bytes > buffer pool limit " |
| 286 | "of $1 bytes", len, system_bytes_limit_)); |
| 287 | } |
| 288 | |
| 289 | const int current_core = CpuInfo::GetCurrentCore(); |
| 290 | // Fast path: recycle a buffer of the correct size from this core's arena. |
| 291 | FreeBufferArena* current_core_arena = per_core_arenas_[current_core].get(); |
| 292 | if (current_core_arena->PopFreeBuffer(len, buffer)) { |
| 293 | current_core_arena->local_arena_free_buffer_hits()->Increment(1); |
| 294 | return Status::OK(); |
| 295 | } |
| 296 | |
| 297 | // Fast-ish path: allocate a new buffer if there is room in 'system_bytes_remaining_'. |
| 298 | int64_t delta = DecreaseBytesRemaining(len, true, &system_bytes_remaining_); |
| 299 | // Whether to record stats about the system alloc (we don't want to do this every |
| 300 | // allocation because of the overhead). |
| 301 | bool sample_sys_alloc_stats = false; |
| 302 | if (delta == len) { |
| 303 | int64_t count = current_core_arena->direct_alloc_count()->Increment(1); |
| 304 | sample_sys_alloc_stats = count % ALLOC_STAT_SAMPLE_RATE == 0; |
| 305 | } else { |
| 306 | DCHECK_EQ(0, delta); |
| 307 | const vector<int>& numa_node_cores = CpuInfo::GetCoresOfSameNumaNode(current_core); |
| 308 | const int numa_node_core_idx = CpuInfo::GetNumaNodeCoreIdx(current_core); |
| 309 | |
| 310 | // Fast-ish path: find a buffer of the right size from another core on the same |
| 311 | // NUMA node. Avoid getting a buffer from another NUMA node - prefer reclaiming |
| 312 | // a clean page on this NUMA node or scavenging then reallocating a new buffer. |
| 313 | // We don't want to get into a state where allocations between the nodes are |
| 314 | // unbalanced and one node is stuck reusing memory allocated on the other node. |
| 315 | for (int i = 1; i < numa_node_cores.size(); ++i) { |
| 316 | // Each core should start searching from a different point to avoid hot-spots. |
| 317 | int other_core = numa_node_cores[(numa_node_core_idx + i) % numa_node_cores.size()]; |
| 318 | FreeBufferArena* other_core_arena = per_core_arenas_[other_core].get(); |
| 319 | if (other_core_arena->PopFreeBuffer(len, buffer)) { |
| 320 | current_core_arena->numa_arena_free_buffer_hits()->Increment(1); |
| 321 | return Status::OK(); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | // Fast-ish path: evict a clean page of the right size from the current NUMA node. |
| 326 | for (int i = 0; i < numa_node_cores.size(); ++i) { |
| 327 | int other_core = numa_node_cores[(numa_node_core_idx + i) % numa_node_cores.size()]; |
| 328 | FreeBufferArena* other_core_arena = per_core_arenas_[other_core].get(); |
| 329 | if (other_core_arena->EvictCleanPage(len, buffer)) { |
| 330 | current_core_arena->clean_page_hits()->Increment(1); |
| 331 | return Status::OK(); |
nothing calls this directly
no test coverage detected