• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <grpc/slice.h>
18 #include <grpc/support/log.h>
19 
20 #include <algorithm>
21 #include <atomic>
22 #include <chrono>
23 #include <random>
24 #include <set>
25 #include <thread>
26 #include <vector>
27 
28 #include "gtest/gtest.h"
29 #include "src/core/lib/iomgr/exec_ctx.h"
30 #include "test/core/resource_quota/call_checker.h"
31 #include "test/core/test_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 
TEST(MemoryQuotaTest,ContainerMemoryAccountedFor)190 TEST(MemoryQuotaTest, ContainerMemoryAccountedFor) {
191   MemoryQuota memory_quota("foo");
192   memory_quota.SetSize(1000000);
193   EXPECT_EQ(ContainerMemoryPressure(), 0.0);
194   auto owner = memory_quota.CreateMemoryOwner();
195   const double original_memory_pressure =
196       owner.GetPressureInfo().instantaneous_pressure;
197   EXPECT_LT(original_memory_pressure, 0.01);
198   SetContainerMemoryPressure(1.0);
199   EXPECT_EQ(owner.GetPressureInfo().instantaneous_pressure, 1.0);
200   SetContainerMemoryPressure(0.0);
201   EXPECT_EQ(owner.GetPressureInfo().instantaneous_pressure,
202             original_memory_pressure);
203   SetContainerMemoryPressure(0.0);
204 }
205 
206 }  // namespace testing
207 
208 namespace memory_quota_detail {
209 namespace testing {
210 
211 //
212 // PressureControllerTest
213 //
214 
TEST(PressureControllerTest,Init)215 TEST(PressureControllerTest, Init) {
216   PressureController c{100, 3};
217   EXPECT_EQ(c.Update(-1.0), 0.0);
218   EXPECT_EQ(c.Update(1.0), 1.0);
219 }
220 
TEST(PressureControllerTest,LowDecays)221 TEST(PressureControllerTest, LowDecays) {
222   PressureController c{100, 3};
223   EXPECT_EQ(c.Update(1.0), 1.0);
224   double last = 1.0;
225   while (last > 1e-30) {
226     double x = c.Update(-1.0);
227     EXPECT_LE(x, last);
228     last = x;
229   }
230 }
231 
232 //
233 // PressureTrackerTest
234 //
235 
TEST(PressureTrackerTest,NoOp)236 TEST(PressureTrackerTest, NoOp) { PressureTracker(); }
237 
TEST(PressureTrackerTest,Decays)238 TEST(PressureTrackerTest, Decays) {
239   PressureTracker tracker;
240   int cur_ms = 0;
241   auto step_time = [&] {
242     ++cur_ms;
243     return Timestamp::ProcessEpoch() + Duration::Seconds(1) +
244            Duration::Milliseconds(cur_ms);
245   };
246   // At start pressure is zero and we should be reading zero back.
247   {
248     ExecCtx exec_ctx;
249     exec_ctx.TestOnlySetNow(step_time());
250     EXPECT_EQ(tracker.AddSampleAndGetControlValue(0.0), 0.0);
251   }
252   // If memory pressure goes to 100% or higher, we should *immediately* snap to
253   // reporting 100%.
254   {
255     ExecCtx exec_ctx;
256     exec_ctx.TestOnlySetNow(step_time());
257     EXPECT_EQ(tracker.AddSampleAndGetControlValue(1.0), 1.0);
258   }
259   // Once memory pressure reduces, we should *eventually* get back to reporting
260   // close to zero, and monotonically decrease.
261   const int got_full = cur_ms;
262   double last_reported = 1.0;
263   while (true) {
264     ExecCtx exec_ctx;
265     exec_ctx.TestOnlySetNow(step_time());
266     double new_reported = tracker.AddSampleAndGetControlValue(0.0);
267     EXPECT_LE(new_reported, last_reported);
268     last_reported = new_reported;
269     if (new_reported < 0.1) break;
270   }
271   // Verify the above happened in a somewhat reasonable time.
272   ASSERT_LE(cur_ms, got_full + 1000000);
273 }
274 
TEST(PressureTrackerTest,ManyThreads)275 TEST(PressureTrackerTest, ManyThreads) {
276   PressureTracker tracker;
277   std::vector<std::thread> threads;
278   std::atomic<bool> shutdown{false};
279   threads.reserve(10);
280   for (int i = 0; i < 10; i++) {
281     threads.emplace_back([&tracker, &shutdown] {
282       std::random_device rng;
283       std::uniform_real_distribution<double> dist(0.0, 1.0);
284       while (!shutdown.load(std::memory_order_relaxed)) {
285         ExecCtx exec_ctx;
286         tracker.AddSampleAndGetControlValue(dist(rng));
287       }
288     });
289   }
290   std::this_thread::sleep_for(std::chrono::seconds(5));
291   shutdown.store(true, std::memory_order_relaxed);
292   for (auto& thread : threads) {
293     thread.join();
294   }
295 }
296 
297 }  // namespace testing
298 }  // namespace memory_quota_detail
299 
300 }  // namespace grpc_core
301 
main(int argc,char ** argv)302 int main(int argc, char** argv) {
303   grpc::testing::TestEnvironment give_me_a_name(&argc, argv);
304   ::testing::InitGoogleTest(&argc, argv);
305   gpr_log_verbosity_init();
306   return RUN_ALL_TESTS();
307 }
308