/*++ Copyright (c) 2015 Minoca Corp. All Rights Reserved Module Name: malloc.c Abstract: This module implements the performance benchmark tests for the malloc() C library routine. Author: Chris Stevens 8-May-2015 Environment: User --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include #include #include #include "perftest.h" // // ---------------------------------------------------------------- Definitions // #define PT_MALLOC_TEST_SMALL_ALLOCATION 32 #define PT_MALLOC_TEST_LARGE_ALLOCATION (128 * 1024) #define PT_MALLOC_TEST_ALLOCATION_LIMIT (256 * 1024) #define PT_MALLOC_TEST_ALLOCATION_COUNT 32 #define PT_MALLOC_TEST_THREAD_COUNT 8 // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // void * MallocStartRoutine ( void *Parameter ); // // -------------------------------------------------------------------- Globals // volatile int MallocReadyThreadCount; // // ------------------------------------------------------------------ Functions // void MallocMain ( PPT_TEST_INFORMATION Test, PPT_TEST_RESULT Result ) /*++ Routine Description: This routine performs the malloc performance benchmark tests. Arguments: Test - Supplies a pointer to the performance test being executed. Result - Supplies a pointer to a performance test result structure that receives the tests results. Return Value: None. --*/ { void **Allocations; size_t AllocationSize; int Index; unsigned long long Iterations; pthread_mutex_t Mutex; int RandomSize; unsigned int Seed; int Status; int ThreadCount; int ThreadIndex; pthread_t *Threads; Iterations = 0; RandomSize = 0; Result->Type = PtResultIterations; Result->Status = 0; ThreadIndex = 0; Threads = NULL; // // Allocate an array to hold the allocations. // AllocationSize = sizeof(void *) * PT_MALLOC_TEST_ALLOCATION_COUNT; Allocations = malloc(AllocationSize); if (Allocations == NULL) { Result->Status = ENOMEM; goto MainEnd; } memset(Allocations, 0, AllocationSize); AllocationSize = 0; // // Figure out the allocation size based on the test. // switch (Test->TestType) { case PtTestMallocSmall: AllocationSize = PT_MALLOC_TEST_SMALL_ALLOCATION; break; case PtTestMallocLarge: AllocationSize = PT_MALLOC_TEST_LARGE_ALLOCATION; break; case PtTestMallocContended: Threads = malloc(sizeof(pthread_t) * PT_MALLOC_TEST_THREAD_COUNT); if (Threads == NULL) { Result->Status = ENOMEM; goto MainEnd; } pthread_mutex_init(&Mutex, NULL); for (ThreadIndex = 0; ThreadIndex < PT_MALLOC_TEST_THREAD_COUNT; ThreadIndex += 1) { Status = pthread_create(&(Threads[ThreadIndex]), NULL, MallocStartRoutine, &Mutex); if (Status != 0) { Result->Status = Status; goto MainEnd; } } // // Wait until all threads are spun up. // while (MallocReadyThreadCount != PT_MALLOC_TEST_THREAD_COUNT) { sleep(1); } RandomSize = 1; break; case PtTestMallocRandom: RandomSize = 1; break; default: assert(0); Result->Status = EINVAL; return; } // // Get a thread-safe seed for the random number generator. // Seed = time(NULL); // // Start the test. This snaps resource usage and starts the clock ticking. // Status = PtStartTimedTest(Test->Duration); if (Status != 0) { Result->Status = errno; goto MainEnd; } // // Measure the performance of the malloc() C library routine by counting // the number of times a memory can be allocated and freed. Randomly keep // around some allocations to make this somewhat realistic. // while (PtIsTimedTestRunning() != 0) { if (RandomSize != 0) { AllocationSize = rand_r(&Seed) % PT_MALLOC_TEST_ALLOCATION_LIMIT; } // // Pick a random allocation slot and either make an allocation if it is // empty or free the existing allocation. // Index = rand_r(&Seed) % PT_MALLOC_TEST_ALLOCATION_COUNT; if (Allocations[Index] == NULL) { Allocations[Index] = malloc(AllocationSize); if (Allocations[Index] == NULL) { Result->Status = ENOMEM; break; } } else { free(Allocations[Index]); Allocations[Index] = NULL; } Iterations += 1; } Status = PtFinishTimedTest(Result); if ((Status != 0) && (Result->Status == 0)) { Result->Status = errno; } MainEnd: switch (Test->TestType) { case PtTestMallocContended: if (Threads != NULL) { ThreadCount = ThreadIndex; for (ThreadIndex = 0; ThreadIndex < ThreadCount; ThreadIndex += 1) { pthread_cancel(Threads[ThreadIndex]); pthread_join(Threads[ThreadIndex], (void **)&Status); if ((Status != 0) && (Result->Status == 0)) { Result->Status = Status; } } free(Threads); } break; case PtTestMallocSmall: case PtTestMallocLarge: case PtTestMallocRandom: default: break; } if (Allocations != NULL) { for (Index = 0; Index < PT_MALLOC_TEST_ALLOCATION_COUNT; Index += 1) { if (Allocations[Index] != NULL) { free(Allocations[Index]); } } free(Allocations); } Result->Data.Iterations = Iterations; return; } // // --------------------------------------------------------- Internal Functions // void * MallocStartRoutine ( void *Parameter ) /*++ Routine Description: This routine implements the start routine for a new test thread. It will wait in a loop for the test to start and then loop allocating and freeing memory regions of random size. Arguments: Parameter - Supplies a pointer to the mutex that protexts the ready thread count. Return Value: 0 on success or an errno value otherwise. --*/ { void **Allocations; int AllocationSize; int Index; pthread_mutex_t *Mutex; unsigned int Seed; AllocationSize = sizeof(void *) * PT_MALLOC_TEST_ALLOCATION_COUNT; Allocations = malloc(AllocationSize); if (Allocations == NULL) { return (void *)ENOMEM; } memset(Allocations, 0, AllocationSize); Seed = time(NULL); // // Announce that this thread is ready. // Mutex = (pthread_mutex_t *)Parameter; pthread_mutex_lock(Mutex); MallocReadyThreadCount += 1; pthread_mutex_unlock(Mutex); // // Busy spin waiting for the test to start. // while (PtIsTimedTestRunning() == 0) { pthread_testcancel(); } // // As long as the test is running, allocation and free blocks of memory. // while (PtIsTimedTestRunning() != 0) { AllocationSize = rand_r(&Seed) % PT_MALLOC_TEST_ALLOCATION_LIMIT; // // Pick a random allocation slot and either make an allocation if it is // empty or free the existing allocation. // Index = rand_r(&Seed) % PT_MALLOC_TEST_ALLOCATION_COUNT; if (Allocations[Index] == NULL) { Allocations[Index] = malloc(AllocationSize); if (Allocations[Index] == NULL) { break; } } else { free(Allocations[Index]); Allocations[Index] = NULL; } } for (Index = 0; Index < PT_MALLOC_TEST_ALLOCATION_COUNT; Index += 1) { if (Allocations[Index] != NULL) { free(Allocations[Index]); } } free(Allocations); return (void *)0; }