VirtualBox

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

Last change on this file since 108651 was 108651, checked in by vboxsync, 4 weeks ago

Runtime/testcase/tstRTMemCache.cpp: Make it work for hosts where we don't know the host page size when building, bugref:10391

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

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