VirtualBox

source: vbox/trunk/src/VBox/Runtime/testcase/tstRTMemCache.cpp@ 96781

Last change on this file since 96781 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.2 KB
Line 
1/* $Id: tstRTMemCache.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT Testcase - RTMemCache.
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/memcache.h>
42
43#include <iprt/asm.h>
44#include <iprt/err.h>
45#include <iprt/initterm.h>
46#include <iprt/mem.h>
47#include <iprt/param.h>
48#include <iprt/rand.h>
49#include <iprt/string.h>
50#include <iprt/semaphore.h>
51#include <iprt/test.h>
52#include <iprt/time.h>
53#include <iprt/thread.h>
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59typedef struct TST3THREAD
60{
61 RTTHREAD hThread;
62 RTSEMEVENTMULTI hEvt;
63 uint64_t volatile cIterations;
64 uint32_t cbObject;
65 bool fUseCache;
66} TST3THREAD, *PTST3THREAD;
67
68
69/*********************************************************************************************************************************
70* Global Variables *
71*********************************************************************************************************************************/
72/** The test handle */
73static RTTEST g_hTest;
74/** Global mem cache handle for use in some of the testcases. */
75static RTMEMCACHE g_hMemCache;
76/** Stop indicator for tst3 threads. */
77static bool volatile g_fTst3Stop;
78
79
80/**
81 * Basic API checks.
82 * We'll return if any of these fails.
83 */
84static void tst1(void)
85{
86 RTTestISub("Basics");
87
88 /* Create one without constructor or destructor. */
89 uint32_t const cObjects = PAGE_SIZE * 2 / 256;
90 RTMEMCACHE hMemCache;
91 RTTESTI_CHECK_RC_RETV(RTMemCacheCreate(&hMemCache, 256, cObjects, 32, NULL, NULL, NULL, 0 /*fFlags*/), VINF_SUCCESS);
92 RTTESTI_CHECK_RETV(hMemCache != NIL_RTMEMCACHE);
93
94 /* Allocate a bit and free it again. */
95 void *pv = NULL;
96 RTTESTI_CHECK_RC_RETV(RTMemCacheAllocEx(hMemCache, &pv), VINF_SUCCESS);
97 RTTESTI_CHECK_RETV(pv != NULL);
98 RTTESTI_CHECK_RETV(RT_ALIGN_P(pv, 32) == pv);
99 RTMemCacheFree(hMemCache, pv);
100
101 RTTESTI_CHECK((pv = RTMemCacheAlloc(hMemCache)) != NULL);
102 RTMemCacheFree(hMemCache, pv);
103
104 /* Allocate everything and free it again, checking size constraints. */
105 for (uint32_t iLoop = 0; iLoop < 20; iLoop++)
106 {
107 /* Allocate everything. */
108 void *apv[cObjects];
109 for (uint32_t i = 0; i < cObjects; i++)
110 {
111 apv[i] = NULL;
112 RTTESTI_CHECK_RC(RTMemCacheAllocEx(hMemCache, &apv[i]), VINF_SUCCESS);
113 }
114
115 /* Check that we've got it all. */
116 int rc;
117 RTTESTI_CHECK_RC(rc = RTMemCacheAllocEx(hMemCache, &pv), VERR_MEM_CACHE_MAX_SIZE);
118 if (RT_SUCCESS(rc))
119 RTMemCacheFree(hMemCache, pv);
120
121 RTTESTI_CHECK((pv = RTMemCacheAlloc(hMemCache)) == NULL);
122 RTMemCacheFree(hMemCache, pv);
123
124 /* Free all the allocations. */
125 for (uint32_t i = 0; i < cObjects; i++)
126 {
127 RTMemCacheFree(hMemCache, apv[i]);
128
129 RTTESTI_CHECK((pv = RTMemCacheAlloc(hMemCache)) != NULL);
130 RTMemCacheFree(hMemCache, pv);
131 }
132 }
133
134 /* Destroy it. */
135 RTTESTI_CHECK_RC(RTMemCacheDestroy(hMemCache), VINF_SUCCESS);
136 RTTESTI_CHECK_RC(RTMemCacheDestroy(NIL_RTMEMCACHE), VINF_SUCCESS);
137}
138
139
140
141/** Constructor for tst2. */
142static DECLCALLBACK(int) tst2Ctor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser)
143{
144 RTTESTI_CHECK(hMemCache == g_hMemCache);
145 RTTESTI_CHECK(ASMMemIsZero(pvObj, 256));
146
147 if (*(bool *)pvUser)
148 return VERR_RESOURCE_BUSY;
149
150 strcat((char *)pvObj, "ctor was called\n");
151 return VINF_SUCCESS;
152}
153
154
155/** Destructor for tst2. Checks that it was constructed and used twice. */
156static DECLCALLBACK(void) tst2Dtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser)
157{
158 RT_NOREF_PV(hMemCache); RT_NOREF_PV(pvUser);
159
160 RTTESTI_CHECK(!strcmp((char *)pvObj, "ctor was called\nused\nused\n"));
161 strcat((char *)pvObj, "dtor was called\n");
162}
163
164/**
165 * Test constructor / destructor.
166 */
167static void tst2(void)
168{
169 RTTestISub("Ctor/Dtor");
170
171 /* Create one without constructor or destructor. */
172 bool fFail = false;
173 uint32_t const cObjects = PAGE_SIZE * 2 / 256;
174 RTTESTI_CHECK_RC_RETV(RTMemCacheCreate(&g_hMemCache, 256, cObjects, 32, tst2Ctor, tst2Dtor, &fFail, 0 /*fFlags*/), VINF_SUCCESS);
175
176 /* A failure run first. */
177 fFail = true;
178 void *pv = (void *)0x42;
179 RTTESTI_CHECK_RC_RETV(RTMemCacheAllocEx(g_hMemCache, &pv), VERR_RESOURCE_BUSY);
180 RTTESTI_CHECK(pv == (void *)0x42);
181 fFail = false;
182
183 /* To two rounds where we allocate all the objects and free them again. */
184 for (uint32_t iLoop = 0; iLoop < 2; iLoop++)
185 {
186 void *apv[cObjects];
187 for (uint32_t i = 0; i < cObjects; i++)
188 {
189 apv[i] = NULL;
190 RTTESTI_CHECK_RC_RETV(RTMemCacheAllocEx(g_hMemCache, &apv[i]), VINF_SUCCESS);
191 if (iLoop == 0)
192 RTTESTI_CHECK(!strcmp((char *)apv[i], "ctor was called\n"));
193 else
194 RTTESTI_CHECK(!strcmp((char *)apv[i], "ctor was called\nused\n"));
195 strcat((char *)apv[i], "used\n");
196 }
197
198 RTTESTI_CHECK_RETV((pv = RTMemCacheAlloc(g_hMemCache)) == NULL);
199 RTMemCacheFree(g_hMemCache, pv);
200
201 for (uint32_t i = 0; i < cObjects; i++)
202 RTMemCacheFree(g_hMemCache, apv[i]);
203 }
204
205 /* Cone, destroy the cache. */
206 RTTESTI_CHECK_RC(RTMemCacheDestroy(g_hMemCache), VINF_SUCCESS);
207}
208
209
210/**
211 * Thread that allocates
212 * @returns
213 * @param hThreadSelf The thread.
214 * @param pvArg Pointer to fUseCache.
215 */
216static DECLCALLBACK(int) tst3Thread(RTTHREAD hThreadSelf, void *pvArg)
217{
218 PTST3THREAD pThread = (PTST3THREAD)(pvArg);
219 size_t cbObject = pThread->cbObject;
220 uint64_t cIterations = 0;
221 RT_NOREF_PV(hThreadSelf);
222
223 /* wait for the kick-off */
224 RTTEST_CHECK_RC_OK(g_hTest, RTSemEventMultiWait(pThread->hEvt, RT_INDEFINITE_WAIT));
225
226 /* allocate and free loop */
227 if (pThread->fUseCache)
228 {
229 while (!g_fTst3Stop)
230 {
231 void *apv[64];
232 for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
233 {
234 apv[i] = RTMemCacheAlloc(g_hMemCache);
235 RTTEST_CHECK(g_hTest, apv[i] != NULL);
236 }
237 for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
238 RTMemCacheFree(g_hMemCache, apv[i]);
239
240 cIterations += RT_ELEMENTS(apv);
241 }
242 }
243 else
244 {
245 while (!g_fTst3Stop)
246 {
247 void *apv[64];
248
249 for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
250 {
251 apv[i] = RTMemAlloc(cbObject);
252 RTTEST_CHECK(g_hTest, apv[i] != NULL);
253 }
254
255 for (unsigned i = 0; i < RT_ELEMENTS(apv); i++)
256 RTMemFree(apv[i]);
257
258 cIterations += RT_ELEMENTS(apv);
259 }
260 }
261
262 /* report back the status */
263 pThread->cIterations = cIterations;
264 return VINF_SUCCESS;
265}
266
267/**
268 * Time constrained test with and unlimited N threads.
269 */
270static void tst3(uint32_t cThreads, uint32_t cbObject, int iMethod, uint32_t cSecs)
271{
272 RTTestISubF("Benchmark - %u threads, %u bytes, %u secs, %s", cThreads, cbObject, cSecs,
273 iMethod == 0 ? "RTMemCache"
274 : "RTMemAlloc");
275
276 /*
277 * Create a cache with unlimited space, a start semaphore and line up
278 * the threads.
279 */
280 RTTESTI_CHECK_RC_RETV(RTMemCacheCreate(&g_hMemCache, cbObject, 0 /*cbAlignment*/, UINT32_MAX, NULL, NULL, NULL, 0 /*fFlags*/), VINF_SUCCESS);
281
282 RTSEMEVENTMULTI hEvt;
283 RTTESTI_CHECK_RC_OK_RETV(RTSemEventMultiCreate(&hEvt));
284
285 TST3THREAD aThreads[64];
286 RTTESTI_CHECK_RETV(cThreads < RT_ELEMENTS(aThreads));
287
288 ASMAtomicWriteBool(&g_fTst3Stop, false);
289 for (uint32_t i = 0; i < cThreads; i++)
290 {
291 aThreads[i].hThread = NIL_RTTHREAD;
292 aThreads[i].cIterations = 0;
293 aThreads[i].fUseCache = iMethod == 0;
294 aThreads[i].cbObject = cbObject;
295 aThreads[i].hEvt = hEvt;
296 RTTESTI_CHECK_RC_OK_RETV(RTThreadCreateF(&aThreads[i].hThread, tst3Thread, &aThreads[i], 0,
297 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "tst3-%u", i));
298 }
299
300 /*
301 * Start the race.
302 */
303 RTTimeNanoTS(); /* warmup */
304
305 uint64_t uStartTS = RTTimeNanoTS();
306 RTTESTI_CHECK_RC_OK_RETV(RTSemEventMultiSignal(hEvt));
307 RTThreadSleep(cSecs * 1000);
308 ASMAtomicWriteBool(&g_fTst3Stop, true);
309 for (uint32_t i = 0; i < cThreads; i++)
310 RTTESTI_CHECK_RC_OK_RETV(RTThreadWait(aThreads[i].hThread, 60*1000, NULL));
311 uint64_t cElapsedNS = RTTimeNanoTS() - uStartTS;
312
313 /*
314 * Sum up the counts.
315 */
316 uint64_t cIterations = 0;
317 for (uint32_t i = 0; i < cThreads; i++)
318 cIterations += aThreads[i].cIterations;
319
320 RTTestIPrintf(RTTESTLVL_ALWAYS, "%'8u iterations per second, %'llu ns on avg\n",
321 (unsigned)((long double)cIterations * 1000000000.0 / (long double)cElapsedNS),
322 cElapsedNS / cIterations);
323
324 /* clean up */
325 RTTESTI_CHECK_RC(RTMemCacheDestroy(g_hMemCache), VINF_SUCCESS);
326 RTTESTI_CHECK_RC_OK(RTSemEventMultiDestroy(hEvt));
327}
328
329static void tst3AllMethods(uint32_t cThreads, uint32_t cbObject, uint32_t cSecs)
330{
331 tst3(cThreads, cbObject, 0, cSecs);
332 tst3(cThreads, cbObject, 1, cSecs);
333}
334
335
336int main(int argc, char **argv)
337{
338 RT_NOREF_PV(argc); RT_NOREF_PV(argv);
339
340 RTTEST hTest;
341 int rc = RTTestInitAndCreate("tstRTMemCache", &hTest);
342 if (rc)
343 return rc;
344 RTTestBanner(hTest);
345 g_hTest = hTest;
346
347 tst1();
348 tst2();
349 if (RTTestIErrorCount() == 0)
350 {
351 uint32_t cSecs = argc == 1 ? 5 : 2;
352 /* threads, cbObj, cSecs */
353 tst3AllMethods( 1, 256, cSecs);
354 tst3AllMethods( 1, 32, cSecs);
355 tst3AllMethods( 1, 8, cSecs);
356 tst3AllMethods( 1, 2, cSecs);
357 tst3AllMethods( 1, 1, cSecs);
358
359 tst3AllMethods( 3, 256, cSecs);
360 tst3AllMethods( 3, 128, cSecs);
361 tst3AllMethods( 3, 64, cSecs);
362 tst3AllMethods( 3, 32, cSecs);
363 tst3AllMethods( 3, 2, cSecs);
364 tst3AllMethods( 3, 1, cSecs);
365
366 tst3AllMethods( 16, 32, cSecs);
367 }
368
369 /*
370 * Summary.
371 */
372 return RTTestSummaryAndDestroy(hTest);
373}
374
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette