Branch data Line data Source code
1 : : // Copyright 2026 HPActor Contributors
2 : : //
3 : : // Licensed under the Apache License, Version 2.0 (the "License");
4 : : // you may not use this file except in compliance with the License.
5 : : // You may obtain a copy of the License at
6 : : //
7 : : // http://www.apache.org/licenses/LICENSE-2.0
8 : : //
9 : : // Unless required by applicable law or agreed to in writing, software
10 : : // distributed under the License is distributed on an "AS IS" BASIS,
11 : : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : // See the License for the specific language governing permissions and
13 : : // limitations under the License.
14 : :
15 : : #include <hpactor/mem/compaction.hpp>
16 : :
17 : : #include <chrono>
18 : :
19 : : namespace hpactor::mem {
20 : :
21 : 6 : bool CompactionManager::should_compact_slab(uint32_t live_blocks,
22 : : uint32_t total_blocks) const noexcept {
23 : 6 : if (total_blocks == 0) return false;
24 : 5 : float utilization = static_cast<float>(live_blocks)
25 : 5 : / static_cast<float>(total_blocks);
26 : 5 : return utilization <= config_.compaction_threshold;
27 : : }
28 : :
29 : : CompactionManager::WasteReport
30 : 1 : CompactionManager::compute_waste(const SlabCache& cache) noexcept {
31 : 1 : WasteReport report;
32 : :
33 : : // Waste = unused bytes in partially-filled slabs
34 : : // For simplicity: report stats from the cache's built-in counters
35 : 1 : const auto& stats = cache.stats();
36 : 1 : report.total_bytes = stats.alloc_count.load(std::memory_order_relaxed)
37 : 1 : * size_for_class(cache.size_class());
38 : :
39 : : // Estimate waste from freed blocks not yet recycled
40 : 1 : uint64_t freed = stats.free_count.load(std::memory_order_relaxed);
41 : 1 : uint64_t allocated = stats.alloc_count.load(std::memory_order_relaxed);
42 : 1 : if (allocated > freed) {
43 : 1 : uint64_t live = allocated - freed;
44 : : // Slabs have fixed total blocks; compute wasted space
45 : : // Blocks per slab for each size class (64KB base / block_size)
46 : : static constexpr uint32_t kSlabBlocks[kNumSizeClasses] = {
47 : : 2048, 1024, 512, 512, 512, 256, 256, 128
48 : : };
49 : 1 : uint64_t slab_blocks = kSlabBlocks[static_cast<uint8_t>(cache.size_class())];
50 : 1 : uint64_t slabs_needed = (live + slab_blocks - 1) / slab_blocks;
51 : 1 : uint64_t full_capacity = slabs_needed * slab_blocks
52 : 1 : * size_for_class(cache.size_class());
53 : 1 : report.wasted_bytes = (full_capacity > report.total_bytes)
54 : 1 : ? (full_capacity - report.total_bytes) : 0;
55 : : }
56 : :
57 : 1 : if (report.total_bytes > 0) {
58 : 1 : report.waste_ratio = static_cast<float>(report.wasted_bytes)
59 : 1 : / static_cast<float>(report.total_bytes);
60 : : }
61 : 1 : return report;
62 : : }
63 : :
64 : 2 : bool CompactionManager::should_compact() const noexcept {
65 : 2 : auto now = std::chrono::steady_clock::now();
66 : 2 : auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
67 : 4 : now.time_since_epoch()).count();
68 : 2 : return (now_ms - last_compaction_ts_)
69 : 2 : >= static_cast<int64_t>(config_.compaction_interval_ms);
70 : : }
71 : :
72 : 1 : void CompactionManager::record_compaction() noexcept {
73 : 1 : auto now = std::chrono::steady_clock::now();
74 : 1 : last_compaction_ts_ = std::chrono::duration_cast<std::chrono::milliseconds>(
75 : 2 : now.time_since_epoch()).count();
76 : 1 : }
77 : :
78 : : } // namespace hpactor::mem
|