• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
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 #ifndef PANDA_RUNTIME_MEM_GC_GC_THRESHOLD_H
16 #define PANDA_RUNTIME_MEM_GC_GC_THRESHOLD_H
17 
18 #include <cstddef>
19 #include <cstdint>
20 #include <string_view>
21 
22 #include "libpandabase/macros.h"
23 #include "libpandabase/utils/ring_buffer.h"
24 #include "runtime/mem/gc/gc.h"
25 
26 namespace panda {
27 
28 class RuntimeOptions;
29 
30 namespace test {
31 class GCTriggerTest;
32 }  // namespace test
33 
34 namespace mem {
35 
36 enum class GCTriggerType {
37     INVALID_TRIGGER,
38     HEAP_TRIGGER_TEST,       // TRIGGER with low thresholds for tests
39     HEAP_TRIGGER,            // Standard TRIGGER with production ready thresholds
40     ADAPTIVE_HEAP_TRIGGER,   // TRIGGER with adaptive strategy for heap increasing
41     NO_GC_FOR_START_UP,      // A non-production strategy, TRIGGER GC after the app starts up
42     TRIGGER_HEAP_OCCUPANCY,  // Trigger full GC by heap size threshold
43     DEBUG,                   // Debug TRIGGER which always returns true
44     DEBUG_NEVER,             // Trigger for testing which never triggers (young-gc can still trigger), for test purpose
45     GCTRIGGER_LAST = DEBUG
46 };
47 
48 class GCTriggerConfig {
49 public:
50     GCTriggerConfig(const RuntimeOptions &options, panda_file::SourceLang lang);
51 
GetGCTriggerType()52     std::string_view GetGCTriggerType() const
53     {
54         return gc_trigger_type_;
55     }
56 
GetDebugStart()57     uint64_t GetDebugStart() const
58     {
59         return debug_start_;
60     }
61 
GetPercentThreshold()62     uint32_t GetPercentThreshold() const
63     {
64         return percent_threshold_;
65     }
66 
GetAdaptiveMultiplier()67     uint32_t GetAdaptiveMultiplier() const
68     {
69         return adaptive_multiplier_;
70     }
71 
GetMinExtraHeapSize()72     size_t GetMinExtraHeapSize() const
73     {
74         return min_extra_heap_size_;
75     }
76 
GetMaxExtraHeapSize()77     size_t GetMaxExtraHeapSize() const
78     {
79         return max_extra_heap_size_;
80     }
81 
GetMaxTriggerPercent()82     uint32_t GetMaxTriggerPercent() const
83     {
84         return max_trigger_percent_;
85     }
86 
GetSkipStartupGcCount()87     uint32_t GetSkipStartupGcCount() const
88     {
89         return skip_startup_gc_count_;
90     }
91 
92 private:
93     std::string gc_trigger_type_;
94     uint64_t debug_start_;
95     uint32_t percent_threshold_;
96     uint32_t adaptive_multiplier_;
97     size_t min_extra_heap_size_;
98     size_t max_extra_heap_size_;
99     uint32_t max_trigger_percent_;
100     uint32_t skip_startup_gc_count_;
101 };
102 
103 class GCTrigger : public GCListener {
104 public:
GCTrigger(HeapSpace * heap_space)105     explicit GCTrigger(HeapSpace *heap_space) : heap_space_(heap_space) {}
106     ~GCTrigger() override;
107     NO_COPY_SEMANTIC(GCTrigger);
108     NO_MOVE_SEMANTIC(GCTrigger);
109 
110     /**
111      * \brief Checks if GC required
112      * @return returns true if GC should be executed
113      */
114     virtual bool IsGcTriggered() = 0;
SetMinTargetFootprint(size_t heap_size)115     virtual void SetMinTargetFootprint([[maybe_unused]] size_t heap_size) {}
RestoreMinTargetFootprint()116     virtual void RestoreMinTargetFootprint() {}
117 
118 protected:
119     HeapSpace *heap_space_ {nullptr};  // NOLINT(misc-non-private-member-variables-in-classes)
120 private:
121     friend class GC;
122 };
123 
124 /**
125  * Triggers when heap increased by predefined %
126  */
127 class GCTriggerHeap : public GCTrigger {
128 public:
129     explicit GCTriggerHeap(MemStatsType *mem_stats, HeapSpace *heap_space);
130     explicit GCTriggerHeap(MemStatsType *mem_stats, HeapSpace *heap_space, size_t min_heap_size,
131                            uint8_t percent_threshold, size_t min_extra_size, size_t max_extra_size,
132                            uint32_t skip_gc_times = 0);
133 
134     bool IsGcTriggered() override;
135 
136     void GCStarted(size_t heap_size) override;
137     void GCFinished(const GCTask &task, size_t heap_size_before_gc, size_t heap_size) override;
138     void SetMinTargetFootprint(size_t target_size) override;
139     void RestoreMinTargetFootprint() override;
140     void ComputeNewTargetFootprint(const GCTask &task, size_t heap_size_before_gc, size_t heap_size);
141 
142     static constexpr uint8_t DEFAULT_PERCENTAGE_THRESHOLD = 20;
143 
144 protected:
145     // TODO(dtrubenkov): change to the proper value when all triggers will be enabled
146     static constexpr size_t MIN_HEAP_SIZE_FOR_TRIGGER = 512;
147     static constexpr size_t DEFAULT_MIN_TARGET_FOOTPRINT = 256;
148     static constexpr size_t DEFAULT_MIN_EXTRA_HEAP_SIZE = 32;      // For heap-trigger-test
149     static constexpr size_t DEFAULT_MAX_EXTRA_HEAP_SIZE = 512_KB;  // For heap-trigger-test
150 
151     virtual size_t ComputeTarget(size_t heap_size_before_gc, size_t heap_size);
152 
153     // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
154     std::atomic<size_t> target_footprint_ {MIN_HEAP_SIZE_FOR_TRIGGER};
155 
156     /**
157      * We'll trigger if heap increased by delta, delta = heap_size_after_last_gc * percent_threshold_ %
158      * And the constraint on delta is: min_extra_size_ <= delta <= max_extra_size_
159      */
160     uint8_t percent_threshold_ {DEFAULT_PERCENTAGE_THRESHOLD};  // NOLINT(misc-non-private-member-variables-in-classes)
161     size_t min_extra_size_ {DEFAULT_MIN_EXTRA_HEAP_SIZE};       // NOLINT(misc-non-private-member-variables-in-classes)
162     size_t max_extra_size_ {DEFAULT_MAX_EXTRA_HEAP_SIZE};       // NOLINT(misc-non-private-member-variables-in-classes)
163 
164 private:
165     size_t min_target_footprint_ {DEFAULT_MIN_TARGET_FOOTPRINT};
166     MemStatsType *mem_stats_;
167     uint8_t skip_gc_count_ {0};
168 
169     friend class panda::test::GCTriggerTest;
170 };
171 
172 /**
173  * Triggers when heap increased by adaptive strategy
174  */
175 class GCAdaptiveTriggerHeap : public GCTriggerHeap {
176 public:
177     GCAdaptiveTriggerHeap(MemStatsType *mem_stats, HeapSpace *heap_space, size_t min_heap_size,
178                           uint8_t percent_threshold, uint32_t adaptive_multiplier, size_t min_extra_size,
179                           size_t max_extra_size, uint32_t skip_gc_times = 0);
180     NO_COPY_SEMANTIC(GCAdaptiveTriggerHeap);
181     NO_MOVE_SEMANTIC(GCAdaptiveTriggerHeap);
182     ~GCAdaptiveTriggerHeap() override = default;
183 
184 private:
185     static constexpr uint32_t DEFAULT_INCREASE_MULTIPLIER = 3U;
186     // We save last RECENT_THRESHOLDS_COUNT thresholds for detect too often triggering
187     static constexpr size_t RECENT_THRESHOLDS_COUNT = 3;
188 
189     size_t ComputeTarget(size_t heap_size_before_gc, size_t heap_size) override;
190 
191     RingBuffer<size_t, RECENT_THRESHOLDS_COUNT> recent_target_thresholds_;
192     uint32_t adaptive_multiplier_ {DEFAULT_INCREASE_MULTIPLIER};
193 
194     friend class panda::test::GCTriggerTest;
195 };
196 
197 /**
198  * Trigger always returns true after given start
199  */
200 class GCTriggerDebug : public GCTrigger {
201 public:
202     explicit GCTriggerDebug(uint64_t debug_start, HeapSpace *heap_space);
203 
204     bool IsGcTriggered() override;
205 
206     void GCStarted(size_t heap_size) override;
207     void GCFinished(const GCTask &task, size_t heap_size_before_gc, size_t heap_size) override;
208 
209 private:
210     uint64_t debug_start_ = 0;
211 };
212 
213 class GCTriggerHeapOccupancy : public GCTrigger {
214 public:
215     explicit GCTriggerHeapOccupancy(HeapSpace *heap_space, uint32_t max_trigger_percent);
216 
217     bool IsGcTriggered() override;
218 
219     void GCStarted([[maybe_unused]] size_t heap_size) override;
220     void GCFinished([[maybe_unused]] const GCTask &task, [[maybe_unused]] size_t heap_size_before_gc,
221                     [[maybe_unused]] size_t heap_size) override;
222 
223 private:
224     double max_trigger_percent_ = 0.0;
225 };
226 
227 class GCNeverTrigger : public GCTrigger {
228 public:
GCNeverTrigger(HeapSpace * heap_space)229     explicit GCNeverTrigger(HeapSpace *heap_space) : GCTrigger(heap_space) {}
230 
IsGcTriggered()231     bool IsGcTriggered() override
232     {
233         return false;
234     }
235 
GCStarted(size_t heap_size)236     void GCStarted([[maybe_unused]] size_t heap_size) override {}
GCFinished(const GCTask & task,size_t heap_size_before_gc,size_t heap_size)237     void GCFinished([[maybe_unused]] const GCTask &task, [[maybe_unused]] size_t heap_size_before_gc,
238                     [[maybe_unused]] size_t heap_size) override
239     {
240     }
241 };
242 
243 GCTrigger *CreateGCTrigger(MemStatsType *mem_stats, HeapSpace *heap_space, const GCTriggerConfig &config,
244                            InternalAllocatorPtr allocator);
245 
246 }  // namespace mem
247 }  // namespace panda
248 
249 #endif  // PANDA_RUNTIME_MEM_GC_GC_THRESHOLD_H
250