1 /*
2  * $Id: t-test2.c,v 1.3 2004/11/04 15:01:05 wg Exp $
3  * by Wolfram Gloger 1996-1999, 2001, 2004
4  * A multi-thread test for malloc performance, maintaining a single
5  * global pool of allocated bins.
6  */
7 
8 #if (defined __STDC__ && __STDC__) || defined __cplusplus
9 # include <stdlib.h>
10 #endif
11 #include <stdio.h>
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <sys/time.h>
15 #include <sys/resource.h>
16 #include <sys/wait.h>
17 
18 #if !USE_MALLOC
19 #include <malloc.h>
20 #else
21 #include "malloc.h"
22 #endif
23 
24 #include "lran2.h"
25 #include "t-test.h"
26 
27 struct user_data {
28 	int max;
29 	unsigned long size;
30 	long seed;
31 };
32 #include "thread-st.h"
33 #include "malloc-machine.h" /* for mutex */
34 
35 #define N_TOTAL		10
36 #ifndef N_THREADS
37 #define N_THREADS	2
38 #endif
39 #ifndef N_TOTAL_PRINT
40 #define N_TOTAL_PRINT 50
41 #endif
42 #define STACKSIZE	32768
43 #ifndef MEMORY
44 #define MEMORY		8000000l
45 #endif
46 #define SIZE		10000
47 #define I_MAX		10000
48 #define BINS_PER_BLOCK 20
49 
50 #define RANDOM(d,s)	(lran2(d) % (s))
51 
52 struct block {
53 	struct bin b[BINS_PER_BLOCK];
54 	mutex_t mutex;
55 } *blocks;
56 
57 int n_blocks;
58 
59 #if TEST > 0
60 
61 void
bin_test(void)62 bin_test(void)
63 {
64 	int b, i;
65 
66 	for(b=0; b<n_blocks; b++) {
67 		mutex_lock(&blocks[b].mutex);
68 		for(i=0; i<BINS_PER_BLOCK; i++) {
69 			if(mem_check(blocks[b].b[i].ptr, blocks[b].b[i].size)) {
70 				printf("memory corrupt!\n");
71 				exit(1);
72 			}
73 		}
74 		mutex_unlock(&blocks[b].mutex);
75 	}
76 }
77 
78 #endif
79 
80 void
malloc_test(struct thread_st * st)81 malloc_test(struct thread_st *st)
82 {
83 	struct block *bl;
84 	int i, b, r;
85 	struct lran2_st ld; /* data for random number generator */
86 	unsigned long rsize[BINS_PER_BLOCK];
87 	int rnum[BINS_PER_BLOCK];
88 
89 	lran2_init(&ld, st->u.seed);
90 	for(i=0; i<=st->u.max;) {
91 #if TEST > 1
92 		bin_test();
93 #endif
94 		bl = &blocks[RANDOM(&ld, n_blocks)];
95 		r = RANDOM(&ld, 1024);
96 		if(r < 200) { /* free only */
97 			mutex_lock(&bl->mutex);
98 			for(b=0; b<BINS_PER_BLOCK; b++)
99 				bin_free(&bl->b[b]);
100 			mutex_unlock(&bl->mutex);
101 			i += BINS_PER_BLOCK;
102 		} else { /* alloc/realloc */
103 			/* Generate random numbers in advance. */
104 			for(b=0; b<BINS_PER_BLOCK; b++) {
105 				rsize[b] = RANDOM(&ld, st->u.size) + 1;
106 				rnum[b] = lran2(&ld);
107 			}
108 			mutex_lock(&bl->mutex);
109 			for(b=0; b<BINS_PER_BLOCK; b++)
110 				bin_alloc(&bl->b[b], rsize[b], rnum[b]);
111 			mutex_unlock(&bl->mutex);
112 			i += BINS_PER_BLOCK;
113 		}
114 #if TEST > 2
115 		bin_test();
116 #endif
117 	}
118 }
119 
120 int n_total=0, n_total_max=N_TOTAL, n_running;
121 
122 int
my_end_thread(struct thread_st * st)123 my_end_thread(struct thread_st *st)
124 {
125 	/* Thread st has finished.  Start a new one. */
126 #if 0
127 	printf("Thread %lx terminated.\n", (long)st->id);
128 #endif
129 	if(n_total >= n_total_max) {
130 		n_running--;
131 	} else if(st->u.seed++, thread_create(st)) {
132 		printf("Creating thread #%d failed.\n", n_total);
133 	} else {
134 		n_total++;
135 		if(n_total%N_TOTAL_PRINT == 0)
136 			printf("n_total = %d\n", n_total);
137 	}
138 	return 0;
139 }
140 
141 int
main(int argc,char * argv[])142 main(int argc, char *argv[])
143 {
144 	int i, j, bins;
145 	int n_thr=N_THREADS;
146 	int i_max=I_MAX;
147 	unsigned long size=SIZE;
148 	struct thread_st *st;
149 
150 #if USE_MALLOC && USE_STARTER==2
151 	ptmalloc_init();
152 	printf("ptmalloc_init\n");
153 #endif
154 
155 	if(argc > 1) n_total_max = atoi(argv[1]);
156 	if(n_total_max < 1) n_thr = 1;
157 	if(argc > 2) n_thr = atoi(argv[2]);
158 	if(n_thr < 1) n_thr = 1;
159 	if(n_thr > 100) n_thr = 100;
160 	if(argc > 3) i_max = atoi(argv[3]);
161 
162 	if(argc > 4) size = atol(argv[4]);
163 	if(size < 2) size = 2;
164 
165 	bins = MEMORY/size;
166 	if(argc > 5) bins = atoi(argv[5]);
167 	if(bins < BINS_PER_BLOCK) bins = BINS_PER_BLOCK;
168 
169 	n_blocks = bins/BINS_PER_BLOCK;
170 	blocks = (struct block *)malloc(n_blocks*sizeof(*blocks));
171 	if(!blocks)
172 		exit(1);
173 
174 	thread_init();
175 	printf("total=%d threads=%d i_max=%d size=%ld bins=%d\n",
176 		   n_total_max, n_thr, i_max, size, n_blocks*BINS_PER_BLOCK);
177 
178 	for(i=0; i<n_blocks; i++) {
179 		mutex_init(&blocks[i].mutex);
180 		for(j=0; j<BINS_PER_BLOCK; j++) blocks[i].b[j].size = 0;
181 	}
182 
183 	st = (struct thread_st *)malloc(n_thr*sizeof(*st));
184 	if(!st) exit(-1);
185 
186 #if !defined NO_THREADS && (defined __sun__ || defined sun)
187 	/* I know of no other way to achieve proper concurrency with Solaris. */
188 	thr_setconcurrency(n_thr);
189 #endif
190 
191 	/* Start all n_thr threads. */
192 	for(i=0; i<n_thr; i++) {
193 		st[i].u.max = i_max;
194 		st[i].u.size = size;
195 		st[i].u.seed = ((long)i_max*size + i) ^ n_blocks;
196 		st[i].sp = 0;
197 		st[i].func = malloc_test;
198 		if(thread_create(&st[i])) {
199 			printf("Creating thread #%d failed.\n", i);
200 			n_thr = i;
201 			break;
202 		}
203 		printf("Created thread %lx.\n", (long)st[i].id);
204 	}
205 
206 	for(n_running=n_total=n_thr; n_running>0;) {
207 		wait_for_thread(st, n_thr, my_end_thread);
208 	}
209 
210 	for(i=0; i<n_blocks; i++) {
211 		for(j=0; j<BINS_PER_BLOCK; j++)
212 			bin_free(&blocks[i].b[j]);
213 	}
214 
215 	for(i=0; i<n_thr; i++) {
216 		free(st[i].sp);
217 	}
218 	free(st);
219 	free(blocks);
220 #if USE_MALLOC
221 	malloc_stats();
222 #endif
223 	printf("Done.\n");
224 	return 0;
225 }
226 
227 /*
228  * Local variables:
229  * tab-width: 4
230  * End:
231  */
232