xref: /aosp_15_r20/external/grpc-grpc/test/core/resource_quota/memory_quota_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 // Copyright 2021 gRPC authors.
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 "src/core/lib/resource_quota/memory_quota.h"
16 
17 #include <algorithm>
18 #include <atomic>
19 #include <chrono>
20 #include <random>
21 #include <set>
22 #include <thread>
23 #include <vector>
24 
25 #include "gtest/gtest.h"
26 
27 #include <grpc/slice.h>
28 
29 #include "src/core/lib/iomgr/exec_ctx.h"
30 #include "test/core/resource_quota/call_checker.h"
31 #include "test/core/util/test_config.h"
32 
33 namespace grpc_core {
34 namespace testing {
35 
36 //
37 // Helpers
38 //
39 
40 template <size_t kSize>
41 struct Sized {
42   char blah[kSize];
~Sizedgrpc_core::testing::Sized43   virtual ~Sized() {}
44 };
45 
46 //
47 // MemoryRequestTest
48 //
49 
TEST(MemoryRequestTest,ConversionFromSize)50 TEST(MemoryRequestTest, ConversionFromSize) {
51   MemoryRequest request = 3;
52   EXPECT_EQ(request.min(), 3);
53   EXPECT_EQ(request.max(), 3);
54 }
55 
TEST(MemoryRequestTest,MinMax)56 TEST(MemoryRequestTest, MinMax) {
57   MemoryRequest request(3, 7);
58   EXPECT_EQ(request.min(), 3);
59   EXPECT_EQ(request.max(), 7);
60 }
61 
62 //
63 // MemoryQuotaTest
64 //
65 
TEST(MemoryQuotaTest,NoOp)66 TEST(MemoryQuotaTest, NoOp) { MemoryQuota("foo"); }
67 
TEST(MemoryQuotaTest,CreateAllocatorNoOp)68 TEST(MemoryQuotaTest, CreateAllocatorNoOp) {
69   MemoryQuota memory_quota("foo");
70   auto memory_allocator = memory_quota.CreateMemoryAllocator("bar");
71 }
72 
TEST(MemoryQuotaTest,CreateObjectFromAllocator)73 TEST(MemoryQuotaTest, CreateObjectFromAllocator) {
74   ExecCtx exec_ctx;
75   MemoryQuota memory_quota("foo");
76   auto memory_allocator = memory_quota.CreateMemoryAllocator("bar");
77   auto object = memory_allocator.MakeUnique<Sized<4096>>();
78 }
79 
TEST(MemoryQuotaTest,CreateSomeObjectsAndExpectReclamation)80 TEST(MemoryQuotaTest, CreateSomeObjectsAndExpectReclamation) {
81   ExecCtx exec_ctx;
82 
83   MemoryQuota memory_quota("foo");
84   memory_quota.SetSize(4096);
85   auto memory_allocator = memory_quota.CreateMemoryOwner();
86   auto object = memory_allocator.MakeUnique<Sized<2048>>();
87 
88   auto checker1 = CallChecker::Make();
89   memory_allocator.PostReclaimer(
90       ReclamationPass::kDestructive,
91       [&object, checker1](absl::optional<ReclamationSweep> sweep) {
92         checker1->Called();
93         EXPECT_TRUE(sweep.has_value());
94         object.reset();
95       });
96   auto object2 = memory_allocator.MakeUnique<Sized<2048>>();
97   exec_ctx.Flush();
98   EXPECT_EQ(object.get(), nullptr);
99 
100   auto checker2 = CallChecker::Make();
101   memory_allocator.PostReclaimer(
102       ReclamationPass::kDestructive,
103       [&object2, checker2](absl::optional<ReclamationSweep> sweep) {
104         checker2->Called();
105         EXPECT_TRUE(sweep.has_value());
106         object2.reset();
107       });
108   auto object3 = memory_allocator.MakeUnique<Sized<2048>>();
109   exec_ctx.Flush();
110   EXPECT_EQ(object2.get(), nullptr);
111 }
112 
TEST(MemoryQuotaTest,ReserveRangeNoPressure)113 TEST(MemoryQuotaTest, ReserveRangeNoPressure) {
114   MemoryQuota memory_quota("foo");
115   auto memory_allocator = memory_quota.CreateMemoryAllocator("bar");
116   size_t total = 0;
117   for (int i = 0; i < 10000; i++) {
118     ExecCtx exec_ctx;
119     auto n = memory_allocator.Reserve(MemoryRequest(100, 40000));
120     EXPECT_EQ(n, 40000);
121     total += n;
122   }
123   memory_allocator.Release(total);
124 }
125 
TEST(MemoryQuotaTest,MakeSlice)126 TEST(MemoryQuotaTest, MakeSlice) {
127   MemoryQuota memory_quota("foo");
128   auto memory_allocator = memory_quota.CreateMemoryAllocator("bar");
129   std::vector<grpc_slice> slices;
130   for (int i = 1; i < 1000; i++) {
131     ExecCtx exec_ctx;
132     int min = i;
133     int max = 10 * i - 9;
134     slices.push_back(memory_allocator.MakeSlice(MemoryRequest(min, max)));
135   }
136   ExecCtx exec_ctx;
137   for (grpc_slice slice : slices) {
138     grpc_slice_unref(slice);
139   }
140 }
141 
TEST(MemoryQuotaTest,ContainerAllocator)142 TEST(MemoryQuotaTest, ContainerAllocator) {
143   ExecCtx exec_ctx;
144   MemoryQuota memory_quota("foo");
145   auto memory_allocator = memory_quota.CreateMemoryAllocator("bar");
146   Vector<int> vec(&memory_allocator);
147   for (int i = 0; i < 100000; i++) {
148     vec.push_back(i);
149   }
150 }
151 
TEST(MemoryQuotaTest,NoBunchingIfIdle)152 TEST(MemoryQuotaTest, NoBunchingIfIdle) {
153   // Ensure that we don't queue up useless reclamations even if there are no
154   // memory reclamations needed.
155   MemoryQuota memory_quota("foo");
156   std::atomic<size_t> count_reclaimers_called{0};
157 
158   for (size_t i = 0; i < 10000; i++) {
159     ExecCtx exec_ctx;
160     auto memory_owner = memory_quota.CreateMemoryOwner();
161     memory_owner.PostReclaimer(
162         ReclamationPass::kDestructive,
163         [&count_reclaimers_called](absl::optional<ReclamationSweep> sweep) {
164           EXPECT_FALSE(sweep.has_value());
165           count_reclaimers_called.fetch_add(1, std::memory_order_relaxed);
166         });
167     auto object = memory_owner.MakeUnique<Sized<2048>>();
168   }
169 
170   EXPECT_GE(count_reclaimers_called.load(std::memory_order_relaxed), 8000);
171 }
172 
TEST(MemoryQuotaTest,AllMemoryQuotas)173 TEST(MemoryQuotaTest, AllMemoryQuotas) {
174   auto gather = []() {
175     std::set<std::string> all_names;
176     for (const auto& q : AllMemoryQuotas()) {
177       all_names.emplace(q->name());
178     }
179     return all_names;
180   };
181 
182   auto m1 = MakeMemoryQuota("m1");
183   auto m2 = MakeMemoryQuota("m2");
184 
185   EXPECT_EQ(gather(), std::set<std::string>({"m1", "m2"}));
186   m1.reset();
187   EXPECT_EQ(gather(), std::set<std::string>({"m2"}));
188 }
189 
190 }  // namespace testing
191 
192 namespace memory_quota_detail {
193 namespace testing {
194 
195 //
196 // PressureControllerTest
197 //
198 
TEST(PressureControllerTest,Init)199 TEST(PressureControllerTest, Init) {
200   PressureController c{100, 3};
201   EXPECT_EQ(c.Update(-1.0), 0.0);
202   EXPECT_EQ(c.Update(1.0), 1.0);
203 }
204 
TEST(PressureControllerTest,LowDecays)205 TEST(PressureControllerTest, LowDecays) {
206   PressureController c{100, 3};
207   EXPECT_EQ(c.Update(1.0), 1.0);
208   double last = 1.0;
209   while (last > 1e-30) {
210     double x = c.Update(-1.0);
211     EXPECT_LE(x, last);
212     last = x;
213   }
214 }
215 
216 //
217 // PressureTrackerTest
218 //
219 
TEST(PressureTrackerTest,NoOp)220 TEST(PressureTrackerTest, NoOp) { PressureTracker(); }
221 
TEST(PressureTrackerTest,Decays)222 TEST(PressureTrackerTest, Decays) {
223   PressureTracker tracker;
224   int cur_ms = 0;
225   auto step_time = [&] {
226     ++cur_ms;
227     return Timestamp::ProcessEpoch() + Duration::Seconds(1) +
228            Duration::Milliseconds(cur_ms);
229   };
230   // At start pressure is zero and we should be reading zero back.
231   {
232     ExecCtx exec_ctx;
233     exec_ctx.TestOnlySetNow(step_time());
234     EXPECT_EQ(tracker.AddSampleAndGetControlValue(0.0), 0.0);
235   }
236   // If memory pressure goes to 100% or higher, we should *immediately* snap to
237   // reporting 100%.
238   {
239     ExecCtx exec_ctx;
240     exec_ctx.TestOnlySetNow(step_time());
241     EXPECT_EQ(tracker.AddSampleAndGetControlValue(1.0), 1.0);
242   }
243   // Once memory pressure reduces, we should *eventually* get back to reporting
244   // close to zero, and monotonically decrease.
245   const int got_full = cur_ms;
246   double last_reported = 1.0;
247   while (true) {
248     ExecCtx exec_ctx;
249     exec_ctx.TestOnlySetNow(step_time());
250     double new_reported = tracker.AddSampleAndGetControlValue(0.0);
251     EXPECT_LE(new_reported, last_reported);
252     last_reported = new_reported;
253     if (new_reported < 0.1) break;
254   }
255   // Verify the above happened in a somewhat reasonable time.
256   ASSERT_LE(cur_ms, got_full + 1000000);
257 }
258 
TEST(PressureTrackerTest,ManyThreads)259 TEST(PressureTrackerTest, ManyThreads) {
260   PressureTracker tracker;
261   std::vector<std::thread> threads;
262   std::atomic<bool> shutdown{false};
263   threads.reserve(10);
264   for (int i = 0; i < 10; i++) {
265     threads.emplace_back([&tracker, &shutdown] {
266       std::random_device rng;
267       std::uniform_real_distribution<double> dist(0.0, 1.0);
268       while (!shutdown.load(std::memory_order_relaxed)) {
269         ExecCtx exec_ctx;
270         tracker.AddSampleAndGetControlValue(dist(rng));
271       }
272     });
273   }
274   std::this_thread::sleep_for(std::chrono::seconds(5));
275   shutdown.store(true, std::memory_order_relaxed);
276   for (auto& thread : threads) {
277     thread.join();
278   }
279 }
280 
281 }  // namespace testing
282 }  // namespace memory_quota_detail
283 
284 }  // namespace grpc_core
285 
main(int argc,char ** argv)286 int main(int argc, char** argv) {
287   grpc::testing::TestEnvironment give_me_a_name(&argc, argv);
288   ::testing::InitGoogleTest(&argc, argv);
289   gpr_log_verbosity_init();
290   return RUN_ALL_TESTS();
291 }
292