diff options
Diffstat (limited to 'src/VBox/Runtime/testcase/tstRTMemCache.cpp')
-rw-r--r-- | src/VBox/Runtime/testcase/tstRTMemCache.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/src/VBox/Runtime/testcase/tstRTMemCache.cpp b/src/VBox/Runtime/testcase/tstRTMemCache.cpp new file mode 100644 index 00000000..24b2d330 --- /dev/null +++ b/src/VBox/Runtime/testcase/tstRTMemCache.cpp @@ -0,0 +1,374 @@ +/* $Id: tstRTMemCache.cpp $ */ +/** @file + * IPRT Testcase - RTMemCache. + */ + +/* + * Copyright (C) 2010-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/memcache.h> + +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/initterm.h> +#include <iprt/mem.h> +#include <iprt/param.h> +#include <iprt/rand.h> +#include <iprt/string.h> +#include <iprt/semaphore.h> +#include <iprt/test.h> +#include <iprt/time.h> +#include <iprt/thread.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct TST3THREAD +{ + RTTHREAD hThread; + RTSEMEVENTMULTI hEvt; + uint64_t volatile cIterations; + uint32_t cbObject; + bool fUseCache; +} TST3THREAD, *PTST3THREAD; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The test handle */ +static RTTEST g_hTest; +/** Global mem cache handle for use in some of the testcases. */ +static RTMEMCACHE g_hMemCache; +/** Stop indicator for tst3 threads. */ +static bool volatile g_fTst3Stop; + + +/** + * Basic API checks. + * We'll return if any of these fails. + */ +static void tst1(void) +{ + RTTestISub("Basics"); + + /* Create one without constructor or destructor. */ + uint32_t const cObjects = PAGE_SIZE * 2 / 256; + RTMEMCACHE hMemCache; + RTTESTI_CHECK_RC_RETV(RTMemCacheCreate(&hMemCache, 256, cObjects, 32, NULL, NULL, NULL, 0 /*fFlags*/), VINF_SUCCESS); + RTTESTI_CHECK_RETV(hMemCache != NIL_RTMEMCACHE); + + /* Allocate a bit and free it again. */ + void *pv = NULL; + RTTESTI_CHECK_RC_RETV(RTMemCacheAllocEx(hMemCache, &pv), VINF_SUCCESS); + RTTESTI_CHECK_RETV(pv != NULL); + RTTESTI_CHECK_RETV(RT_ALIGN_P(pv, 32) == pv); + RTMemCacheFree(hMemCache, pv); + + RTTESTI_CHECK((pv = RTMemCacheAlloc(hMemCache)) != NULL); + RTMemCacheFree(hMemCache, pv); + + /* Allocate everything and free it again, checking size constraints. */ + for (uint32_t iLoop = 0; iLoop < 20; iLoop++) + { + /* Allocate everything. */ + void *apv[cObjects]; + for (uint32_t i = 0; i < cObjects; i++) + { + apv[i] = NULL; + RTTESTI_CHECK_RC(RTMemCacheAllocEx(hMemCache, &apv[i]), VINF_SUCCESS); + } + + /* Check that we've got it all. */ + int rc; + RTTESTI_CHECK_RC(rc = RTMemCacheAllocEx(hMemCache, &pv), VERR_MEM_CACHE_MAX_SIZE); + if (RT_SUCCESS(rc)) + RTMemCacheFree(hMemCache, pv); + + RTTESTI_CHECK((pv = RTMemCacheAlloc(hMemCache)) == NULL); + RTMemCacheFree(hMemCache, pv); + + /* Free all the allocations. */ + for (uint32_t i = 0; i < cObjects; i++) + { + RTMemCacheFree(hMemCache, apv[i]); + + RTTESTI_CHECK((pv = RTMemCacheAlloc(hMemCache)) != NULL); + RTMemCacheFree(hMemCache, pv); + } + } + + /* Destroy it. */ + RTTESTI_CHECK_RC(RTMemCacheDestroy(hMemCache), VINF_SUCCESS); + RTTESTI_CHECK_RC(RTMemCacheDestroy(NIL_RTMEMCACHE), VINF_SUCCESS); +} + + + +/** Constructor for tst2. */ +static DECLCALLBACK(int) tst2Ctor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser) +{ + RTTESTI_CHECK(hMemCache == g_hMemCache); + RTTESTI_CHECK(ASMMemIsZero(pvObj, 256)); + + if (*(bool *)pvUser) + return VERR_RESOURCE_BUSY; + + strcat((char *)pvObj, "ctor was called\n"); + return VINF_SUCCESS; +} + + +/** Destructor for tst2. Checks that it was constructed and used twice. */ +static DECLCALLBACK(void) tst2Dtor(RTMEMCACHE hMemCache, void *pvObj, void *pvUser) +{ + RT_NOREF_PV(hMemCache); RT_NOREF_PV(pvUser); + + RTTESTI_CHECK(!strcmp((char *)pvObj, "ctor was called\nused\nused\n")); + strcat((char *)pvObj, "dtor was called\n"); +} + +/** + * Test constructor / destructor. + */ +static void tst2(void) +{ + RTTestISub("Ctor/Dtor"); + + /* Create one without constructor or destructor. */ + bool fFail = false; + uint32_t const cObjects = PAGE_SIZE * 2 / 256; + RTTESTI_CHECK_RC_RETV(RTMemCacheCreate(&g_hMemCache, 256, cObjects, 32, tst2Ctor, tst2Dtor, &fFail, 0 /*fFlags*/), VINF_SUCCESS); + + /* A failure run first. */ + fFail = true; + void *pv = (void *)0x42; + RTTESTI_CHECK_RC_RETV(RTMemCacheAllocEx(g_hMemCache, &pv), VERR_RESOURCE_BUSY); + RTTESTI_CHECK(pv == (void *)0x42); + fFail = false; + + /* To two rounds where we allocate all the objects and free them again. */ + for (uint32_t iLoop = 0; iLoop < 2; iLoop++) + { + void *apv[cObjects]; + for (uint32_t i = 0; i < cObjects; i++) + { + apv[i] = NULL; + RTTESTI_CHECK_RC_RETV(RTMemCacheAllocEx(g_hMemCache, &apv[i]), VINF_SUCCESS); + if (iLoop == 0) + RTTESTI_CHECK(!strcmp((char *)apv[i], "ctor was called\n")); + else + RTTESTI_CHECK(!strcmp((char *)apv[i], "ctor was called\nused\n")); + strcat((char *)apv[i], "used\n"); + } + + RTTESTI_CHECK_RETV((pv = RTMemCacheAlloc(g_hMemCache)) == NULL); + RTMemCacheFree(g_hMemCache, pv); + + for (uint32_t i = 0; i < cObjects; i++) + RTMemCacheFree(g_hMemCache, apv[i]); + } + + /* Cone, destroy the cache. */ + RTTESTI_CHECK_RC(RTMemCacheDestroy(g_hMemCache), VINF_SUCCESS); +} + + +/** + * Thread that allocates + * @returns + * @param hThreadSelf The thread. + * @param pvArg Pointer to fUseCache. + */ +static DECLCALLBACK(int) tst3Thread(RTTHREAD hThreadSelf, void *pvArg) +{ + PTST3THREAD pThread = (PTST3THREAD)(pvArg); + size_t cbObject = pThread->cbObject; + uint64_t cIterations = 0; + RT_NOREF_PV(hThreadSelf); + + /* wait for the kick-off */ + RTTEST_CHECK_RC_OK(g_hTest, RTSemEventMultiWait(pThread->hEvt, RT_INDEFINITE_WAIT)); + + /* allocate and free loop */ + if (pThread->fUseCache) + { + while (!g_fTst3Stop) + { + void *apv[64]; + for (unsigned i = 0; i < RT_ELEMENTS(apv); i++) + { + apv[i] = RTMemCacheAlloc(g_hMemCache); + RTTEST_CHECK(g_hTest, apv[i] != NULL); + } + for (unsigned i = 0; i < RT_ELEMENTS(apv); i++) + RTMemCacheFree(g_hMemCache, apv[i]); + + cIterations += RT_ELEMENTS(apv); + } + } + else + { + while (!g_fTst3Stop) + { + void *apv[64]; + + for (unsigned i = 0; i < RT_ELEMENTS(apv); i++) + { + apv[i] = RTMemAlloc(cbObject); + RTTEST_CHECK(g_hTest, apv[i] != NULL); + } + + for (unsigned i = 0; i < RT_ELEMENTS(apv); i++) + RTMemFree(apv[i]); + + cIterations += RT_ELEMENTS(apv); + } + } + + /* report back the status */ + pThread->cIterations = cIterations; + return VINF_SUCCESS; +} + +/** + * Time constrained test with and unlimited N threads. + */ +static void tst3(uint32_t cThreads, uint32_t cbObject, int iMethod, uint32_t cSecs) +{ + RTTestISubF("Benchmark - %u threads, %u bytes, %u secs, %s", cThreads, cbObject, cSecs, + iMethod == 0 ? "RTMemCache" + : "RTMemAlloc"); + + /* + * Create a cache with unlimited space, a start semaphore and line up + * the threads. + */ + RTTESTI_CHECK_RC_RETV(RTMemCacheCreate(&g_hMemCache, cbObject, 0 /*cbAlignment*/, UINT32_MAX, NULL, NULL, NULL, 0 /*fFlags*/), VINF_SUCCESS); + + RTSEMEVENTMULTI hEvt; + RTTESTI_CHECK_RC_OK_RETV(RTSemEventMultiCreate(&hEvt)); + + TST3THREAD aThreads[64]; + RTTESTI_CHECK_RETV(cThreads < RT_ELEMENTS(aThreads)); + + ASMAtomicWriteBool(&g_fTst3Stop, false); + for (uint32_t i = 0; i < cThreads; i++) + { + aThreads[i].hThread = NIL_RTTHREAD; + aThreads[i].cIterations = 0; + aThreads[i].fUseCache = iMethod == 0; + aThreads[i].cbObject = cbObject; + aThreads[i].hEvt = hEvt; + RTTESTI_CHECK_RC_OK_RETV(RTThreadCreateF(&aThreads[i].hThread, tst3Thread, &aThreads[i], 0, + RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "tst3-%u", i)); + } + + /* + * Start the race. + */ + RTTimeNanoTS(); /* warmup */ + + uint64_t uStartTS = RTTimeNanoTS(); + RTTESTI_CHECK_RC_OK_RETV(RTSemEventMultiSignal(hEvt)); + RTThreadSleep(cSecs * 1000); + ASMAtomicWriteBool(&g_fTst3Stop, true); + for (uint32_t i = 0; i < cThreads; i++) + RTTESTI_CHECK_RC_OK_RETV(RTThreadWait(aThreads[i].hThread, 60*1000, NULL)); + uint64_t cElapsedNS = RTTimeNanoTS() - uStartTS; + + /* + * Sum up the counts. + */ + uint64_t cIterations = 0; + for (uint32_t i = 0; i < cThreads; i++) + cIterations += aThreads[i].cIterations; + + RTTestIPrintf(RTTESTLVL_ALWAYS, "%'8u iterations per second, %'llu ns on avg\n", + (unsigned)((long double)cIterations * 1000000000.0 / (long double)cElapsedNS), + cElapsedNS / cIterations); + + /* clean up */ + RTTESTI_CHECK_RC(RTMemCacheDestroy(g_hMemCache), VINF_SUCCESS); + RTTESTI_CHECK_RC_OK(RTSemEventMultiDestroy(hEvt)); +} + +static void tst3AllMethods(uint32_t cThreads, uint32_t cbObject, uint32_t cSecs) +{ + tst3(cThreads, cbObject, 0, cSecs); + tst3(cThreads, cbObject, 1, cSecs); +} + + +int main(int argc, char **argv) +{ + RT_NOREF_PV(argc); RT_NOREF_PV(argv); + + RTTEST hTest; + int rc = RTTestInitAndCreate("tstRTMemCache", &hTest); + if (rc) + return rc; + RTTestBanner(hTest); + g_hTest = hTest; + + tst1(); + tst2(); + if (RTTestIErrorCount() == 0) + { + uint32_t cSecs = argc == 1 ? 5 : 2; + /* threads, cbObj, cSecs */ + tst3AllMethods( 1, 256, cSecs); + tst3AllMethods( 1, 32, cSecs); + tst3AllMethods( 1, 8, cSecs); + tst3AllMethods( 1, 2, cSecs); + tst3AllMethods( 1, 1, cSecs); + + tst3AllMethods( 3, 256, cSecs); + tst3AllMethods( 3, 128, cSecs); + tst3AllMethods( 3, 64, cSecs); + tst3AllMethods( 3, 32, cSecs); + tst3AllMethods( 3, 2, cSecs); + tst3AllMethods( 3, 1, cSecs); + + tst3AllMethods( 16, 32, cSecs); + } + + /* + * Summary. + */ + return RTTestSummaryAndDestroy(hTest); +} + |