/*************************************************************************** * * Copyright (c) 1999 Geodesic Systems, Inc. All rights reserved. * * Multi-threaded version of MTS speed test demo program *************************************************************************** */ #if M_NT #include #elif M_SOLARIS || M_HP700 || M_IBM_R6000 || M_DECALPHA #include #if M_IBM_R6000 #include #else #if USE_MALLOC #include "ptmalloc.h" #else #include #endif #endif #endif #include #include "mts.h" /* * MEM_ARRAY_SIZE defines how many mallocs occur before calls to free are * made. MEM_SAVE_SIZE defines what number of mallocs are not freed in * the current free cycle. Increasing the ARRAY_SIZE increases the amount * of memory that is malloced and freed. Decreasing the SAVE_SIZE divisor * also increases the amount of memory needed to run this test. * This test assumes that 1/15th of the mallocs are not freed on each cycle. */ #define MEM_ARRAY_SIZE 100000 #define MEM_SAVE_SIZE (MEM_ARRAY_SIZE / 5) /* First_addr and last_addr are used to compute total heap size for * platforms where sbrk(0) is available. */ unsigned long first_addr; unsigned long last_addr; #define MAX_THREAD_HEAPS 32 #if M_DECALPHA || M_HP700 || M_IBM_R6000 #define MAX_THREADS 32 char *mem_arrays[MAX_THREADS][MEM_ARRAY_SIZE]; int global_num_threads; #else #define MAX_THREADS 50 #if M_NT /* low_marks and high_marks are used to compute total heap size for * platforms where sbrk(0) is not available. */ unsigned long low_marks[MAX_THREADS]; unsigned long high_marks[MAX_THREADS]; #endif #endif #if M_DECALPHA # define THREAD_HANDLE_DEF pthread_t THREAD_HANDLE; #else # define THREAD_HANDLE_DEF # define THREAD_HANDLE #endif #if M_DECALPHA # define mts_get_thread_id(t) pthread_getsequence_np(pthread_self()) #elif M_SOLARIS # define mts_get_thread_id(t) thr_self() #elif M_HP700 || M_IBM_R6000 # define mts_get_thread_id(t) (unsigned long)pthread_self() #elif M_NT # define mts_get_thread_id(t) GetCurrentThreadId() #else # define mts_get_thread_id(t) 0 #endif #if M_NT #include showtime() { static iter = 0; static long firsttime; SYSTEMTIME systime; GetSystemTime(&systime); if (iter == 0) { firsttime = (systime.wHour * 60 * 60 * 1000 + systime.wMinute * 60 * 1000 + systime.wSecond * 1000 + systime.wMilliseconds); iter++; } else { int ms; int secs; int mins = 0; ms = (systime.wHour * 60 * 60 * 1000 + systime.wMinute * 60 * 1000 + systime.wSecond * 1000 + systime.wMilliseconds) - firsttime; secs = ms / 1000; ms = ms % 1000; mins = secs / 60; secs = secs % 60; printf("\nElapsed time: %dm%d.%3.3ds\n", mins, secs, ms); } } #endif #if M_NT DWORD WINAPI thread_func(LPVOID param) #elif M_SOLARIS || M_HP700 || M_DECALPHA || M_IBM_R6000 void *thread_func(void *param) #endif { #if M_DECALPHA || M_HP700 || M_IBM_R6000 char **memory = param; #else char *memory[MEM_ARRAY_SIZE]; #endif int i; int j; int k; int k_max; int repeat = 100; char **mp = memory; #if M_DECALPHA || M_HP700 || M_IBM_R6000 char **mpe = memory + MEM_ARRAY_SIZE; #else char **mpe = memory + sizeof memory / sizeof memory[0]; #endif char **save_start = mpe; char **save_end = mpe; long low_mark; long high_mark; long print_high_mark; unsigned long threadid; int which_heap; THREAD_HANDLE_DEF #if M_DECALPHA || M_HP700 || M_IBM_R6000 if (!param) return (void *)0; #endif threadid = mts_get_thread_id(THREAD_HANDLE); #if MTS_MT which_heap = mts_which_heap(); printf("Thread %d using heap %d\n", threadid, which_heap); #endif /* * low_mark and high_mark hold the lowest and highest pointers to memory * that has been malloced. They are printed out as the difference increases * between them in 1 meg intervals. */ low_mark = (long) malloc (1); #if M_NT /* On NT, keep track of high mark in control version, since we can't use * sbrk(0) at start and end to determine total memory used. */ high_mark = low_mark; print_high_mark = low_mark; #endif free ((char *) low_mark); while (repeat--) { /* Report progess when each 20% of the test is completed */ if (repeat % 20 == 0) printf("Thread %d completed %d%%\n", threadid, 100 - repeat); for (i = 1; i < 1000000; i = i * 3 / 2 + 1) { for (j = i; j; j /= 2) { k_max = 1; /* k_max controls the size allocation bias. Large things are allocated once. Sizes less than 100 are allocated 250 times. */ if (j < 10000) k_max = 10; if (j < 1000) k_max *= 5; if (j < 100) k_max *= 5; for (k = 0; k < k_max; k ++) { if ( ! (*mp ++ = (char *) malloc (j))) { write (2, "? malloc failed\n", sizeof "? malloc failed\n"); _exit (1); } #if M_NT /* set the high_mark */ if (((long) *(mp - 1)) + j > high_mark) high_mark = (long) *(mp - 1) + j; #endif /* while allocating skip over that portion of the buffer that still holds pointers from the previous cycle */ if (mp == save_start) mp = save_end; if (mp >= mpe) /* if we've reached the end of the malloc buffer */ { mp = memory; /* mark the next portion of the buffer */ save_start = save_end; if (save_start >= mpe) save_start = mp; save_end = save_start + MEM_SAVE_SIZE; if (save_end > mpe) save_end = mpe; /* free the bottom and top parts of the buffer. * The bottom part is freed in the order of allocation. * The top part is free in reverse order of allocation. */ while (mp < save_start) free (*mp ++); mp = mpe; while (mp > save_end) free (*--mp); mp = memory; } } } } } /* free the residual allocations */ mpe = mp; mp = memory; while (mp < mpe) free (*mp ++); printf("Thread %d finished\n", mts_get_thread_id(THREAD_HANDLE)); #if M_NT { int i = (unsigned long *)param - low_marks; low_marks[i] = (unsigned long)low_mark; high_marks[i] = (unsigned long)high_mark; } #endif #if M_HP700 || M_DECALPHA || M_IBM_R6000 global_num_threads--; if (global_num_threads <= 0) { printf("All threads finished, thread %d exiting.\n", mts_get_thread_id(THREAD_HANDLE)); #if MTS_MT printf("Total size of all heaps: %ld\n", mts_heap_total_size()); #else last_addr = (unsigned long)sbrk(0); printf("Total heap size: %ld\n", last_addr - first_addr); #endif exit(0); } #endif return 0; } int main(argc, argv) int argc; char *argv[]; { int num_threads = 1; int num_thread_heaps = 1; int df_size = 10; #if M_NT DWORD threadid; HANDLE hthread; HANDLE handles[MAX_THREADS]; #elif M_SOLARIS || M_HP700 || M_DECALPHA || M_IBM_R6000 pthread_t threadid; int hthread; pthread_t handles[MAX_THREADS]; pthread_attr_t attr; #endif long print_threadid; int i; THREAD_HANDLE_DEF #if !M_NT first_addr = (unsigned long)sbrk(0); #endif /* * prints different messages depending on whether this test is compiled * with the MTS library or libc.a */ #if defined(COMPILE_ID) write (1, "Allocating memory using ", sizeof "Allocating memory using " - 1); write (1, COMPILE_ID, sizeof COMPILE_ID - 1); write (1, "\n\n", 2); #endif if (argc > 1) num_threads = atoi((const char *)argv[1]); if (num_threads > MAX_THREADS) { printf("Maximum number of threads is %d.\n", MAX_THREADS); num_threads = MAX_THREADS; } #if M_HP700 || M_DECALPHA || M_IBM_R6000 global_num_threads = num_threads; #endif if (argc > 2) num_thread_heaps = atoi((const char *)argv[2]); else num_thread_heaps = num_threads; if (num_thread_heaps > MAX_THREAD_HEAPS) { printf("Maximum number of heaps is %d.\n", MAX_THREAD_HEAPS); num_thread_heaps = MAX_THREADS; } #if MTS_MT if (argc > 3) df_size = atoi((const char *)argv[3]); printf("Initializing %d thread heaps\n", num_thread_heaps); mts_init_thread_heaps(num_thread_heaps); mts_set_df_size(df_size); if (num_threads >= 10) mts_set_sys_alloc_size(256 * 4096, 256 * 8 * 16 * 4096, 1); printf("Starting %d threads\n", num_threads); #elif USE_MALLOC ptmalloc_init(); #endif #if M_NT showtime(); #endif for (i = 0; i < num_threads; i++) { #if M_NT hthread = CreateThread(NULL, 0, thread_func, (void *)(low_marks + i), 0, &threadid ); if (hthread == NULL) #elif M_DECALPHA pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); hthread = pthread_create(&threadid, &attr, thread_func, (void*)(mem_arrays[i])); if (hthread != 0) #elif M_SOLARIS || M_HP700 || M_IBM_R6000 pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); #if M_HP700 || M_IBM_R6000 hthread = pthread_create(&threadid, &attr, thread_func, (void *)(mem_arrays[i])); #else hthread = pthread_create(&threadid, &attr, thread_func, 0); #endif if (hthread != 0) #endif printf("Cannot start thread, error code: %d\n", hthread); else { #if M_DECALPHA print_threadid = pthread_getsequence_np(pthread_self()); #endif } #if M_NT handles[i] = hthread; #endif } #if M_NT WaitForMultipleObjects(num_threads, handles, TRUE, INFINITE); #elif M_SOLARIS while (thr_join(NULL, NULL, NULL) == 0); #elif M_HP700 || M_DECALPHA || M_IBM_R6000 sleep(100000000); #endif printf("All threads finished.\n"); /* Report the total heap size, i.e. the amount of memory that has been * reserved for this process by calls to sbrk (on Unix) or VirtualAlloc * (on NT). For the MTS version of the test, we call the * routine mts_heap_total_size(), which returns the sum of the amounts * of memory that have been acquired by all of the MTS heaps through * calls to sbrk or VirtualAlloc (MTS keeps track of this in internal * counters, as memory is allocated). For the standard malloc version * of the test, on NT we take the difference between the lowest and highest * pointers returned to this program by malloc, and on Unix we call sbrk(0) * at the beginning and end of this program (giving the address that * would be returned by the next non-zero call to sbrk) and take the * difference between these addresses. The reason we don't use this * same approach to report the heap size for the MTS version is that * MTS allocates substantial memory for its initial heap before main() * is called, so that the approaches used for the standard malloc version * of the test would under-report the amount of memory used by the MTS * version of the test. However, on NT where a non-trivial amount of * code is required to detect the lowest and highest addresses returned * by malloc, the MTS version of the test performs the same logic as * the standard version, to avoid giving the MTS version an unfair * performance advantage. */ #if MTS_MT printf("Total heap size: %ld\n", mts_heap_total_size()); #elif M_NT { int lowest_mark = low_marks[0]; int highest_mark = high_marks[0]; for (i = 1; i < num_threads; i++) { if (low_marks[i] < lowest_mark) lowest_mark = low_marks[i]; if (high_marks[i] > highest_mark) highest_mark = high_marks[i]; } printf("Total foo heap size: %ld\n", highest_mark - lowest_mark); } #else last_addr = (unsigned long)sbrk(0); printf("Total heap size: %ld\n", last_addr - first_addr); #endif #if M_NT showtime(); #endif return 0; }