1 /******************************************************************************
4 * Test harness for the various set implementations.
6 * Copyright (c) 2002-2003, K A Fraser
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
12 * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution. Neither the name of the Keir Fraser
18 * nor the names of its contributors may be used to endorse or
19 * promote products derived from this software without specific
20 * prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/resource.h>
41 #include <sys/types.h>
43 #include <sys/times.h>
54 #include "portable_defns.h"
58 /* This produces an operation log for the 'replay' checker. */
59 /*#define DO_WRITE_LOG*/
62 #define MAX_ITERATIONS 100000
63 #define MAX_WALL_TIME 50 /* seconds */
65 #define MAX_ITERATIONS 100000000
66 #define MAX_WALL_TIME 10 /* seconds */
70 * ***************** LOGGING
73 #define MAX_LOG_RECORDS 256
75 #define LOG_KIND_INT 0
76 #define LOG_KIND_STRING 1
77 #define LOG_KIND_FLOAT 2
87 static log_record_t log_records
[MAX_LOG_RECORDS
];
89 static int num_log_records
= 0;
91 static void log_int (char *name
, int val
) {
92 log_records
[num_log_records
].name
= name
;
93 log_records
[num_log_records
].kind
= LOG_KIND_INT
;
94 log_records
[num_log_records
].val_int
= val
;
98 static void log_string (char *name
, char *val
) {
99 log_records
[num_log_records
].name
= name
;
100 log_records
[num_log_records
].kind
= LOG_KIND_STRING
;
101 log_records
[num_log_records
].val_string
= val
;
105 static void log_float (char *name
, float val
) {
106 log_records
[num_log_records
].name
= name
;
107 log_records
[num_log_records
].kind
= LOG_KIND_FLOAT
;
108 log_records
[num_log_records
].val_float
= val
;
112 static void dump_log (void) {
115 fprintf (stdout
, "-------------------------------------------"
116 "---------------------------\n");
117 for (i
= 0; i
< num_log_records
; i
++)
120 strcpy(padding
, " ");
121 if (30-strlen(log_records
[i
].name
) >= 0){
122 padding
[30-strlen(log_records
[i
].name
)] = '\0';
124 fprintf (stdout
, "%s%s = ", padding
, log_records
[i
].name
);
126 int kind
= log_records
[i
].kind
;
127 if (kind
== LOG_KIND_INT
) {
128 fprintf (stdout
, "%d\n", log_records
[i
].val_int
);
129 } else if (kind
== LOG_KIND_STRING
) {
130 fprintf (stdout
, "%s\n", log_records
[i
].val_string
);
131 } else if (kind
== LOG_KIND_FLOAT
) {
132 fprintf (stdout
, "%.3f\n", log_records
[i
].val_float
);
136 fprintf (stdout
, "-------------------------------------------"
137 "---------------------------\n");
139 for (i
= 0; i
< num_log_records
; i
++)
141 int kind
= log_records
[i
].kind
;
142 if (i
!= 0) { fprintf (stderr
, " "); }
143 if (kind
== LOG_KIND_INT
) {
144 fprintf (stderr
, "%d", log_records
[i
].val_int
);
145 } else if (kind
== LOG_KIND_STRING
) {
146 fprintf (stderr
, "%s", log_records
[i
].val_string
);
147 } else if (kind
== LOG_KIND_FLOAT
) {
148 fprintf (stderr
, "%.3f", log_records
[i
].val_float
);
151 fprintf (stderr
, " LOG\n");
155 * ************** END OF LOGGING
158 #define TVAL(x) ((x.tv_sec * 1000000) + x.tv_usec)
160 /* Log tables. Written out at end-of-day. */
161 typedef struct log_st
163 interval_t start
, end
;
165 void *val
, *old_val
; /* @old_val used by update() and remove() */
167 #define SIZEOF_GLOBAL_LOG (num_threads*MAX_ITERATIONS*sizeof(log_t))
168 static log_t
*global_log
;
169 static interval_t interval
= 0;
171 static bool_t go
= FALSE
;
172 static int threads_initialised1
= 0, max_key
, log_max_key
;
173 static int threads_initialised2
= 0;
174 static int threads_initialised3
= 0;
177 static unsigned long proportion
;
179 static struct timeval start_time
, done_time
;
180 static struct tms start_tms
, done_tms
;
182 static int successes
[MAX_THREADS
];
185 static int processors
[MAX_THREADS
];
188 /* All the variables accessed in the critical main loop. */
197 #define nrand(_r) (((_r) = (_r) * 1103515245) + 12345)
199 static void alarm_handler( int arg
)
201 shared
.alarm_time
= 1;
204 /*int cntr[MAX_THREADS] = { 0 };*/
206 static void *thread_start(void *arg
)
213 log_t
*log
= global_log
+ id
*MAX_ITERATIONS
;
216 unsigned long r
= ((unsigned long)arg
)+3; /*RDTICK();*/
217 unsigned int prop
= proportion
;
218 unsigned int _max_key
= max_key
;
221 i
= processor_bind(P_LWPID
, P_MYID
, processors
[id
], NULL
);
224 printf("Failed to bind to processor %d! (%d)\n", processors
[id
], i
);
231 _init_ptst_subsystem();
232 _init_gc_subsystem();
233 _init_set_subsystem();
234 shared
.set
= set_alloc();
237 /* BARRIER FOR ALL THREADS */
239 int n_id
, id
= threads_initialised1
;
240 while ( (n_id
= CASIO(&threads_initialised1
, id
, id
+1)) != id
)
243 while ( threads_initialised1
!= num_threads
) MB();
246 /* Start search structure off with a well-distributed set of inital keys */
247 for ( i
= (_max_key
/ num_threads
); i
!= 0; i
>>= 1 )
249 for ( k
= i
>> 1; k
< (_max_key
/ num_threads
); k
+= i
)
251 set_update(shared
.set
,
252 k
+ id
* (_max_key
/ num_threads
),
253 (void *)0xdeadbee0, 1);
259 int n_id
, id
= threads_initialised2
;
260 while ( (n_id
= CASIO(&threads_initialised2
, id
, id
+1)) != id
)
263 while ( threads_initialised2
!= num_threads
) MB();
267 (void)signal(SIGALRM
, &alarm_handler
);
268 (void)alarm(MAX_WALL_TIME
);
270 gettimeofday(&start_time
, NULL
);
281 get_interval(my_int
);
283 for ( i
= 0; (i
< MAX_ITERATIONS
) && !shared
.alarm_time
; i
++ )
285 /* O-3: ignore ; 4-11: proportion ; 12: ins/del */
286 k
= (nrand(r
) >> 4) & (_max_key
- 1);
291 if ( ((r
>>4)&255) < prop
)
293 ov
= v
= set_lookup(shared
.set
, k
);
295 else if ( ((r
>>12)&1) )
297 v
= (void *)((r
&~7)|0x8);
298 ov
= set_update(shared
.set
, k
, v
, 1);
303 ov
= set_remove(shared
.set
, k
);
307 get_interval(my_int
);
316 /* BARRIER FOR ALL THREADS */
318 int n_id
, id
= threads_initialised3
;
319 while ( (n_id
= CASIO(&threads_initialised3
, id
, id
+1)) != id
)
322 while ( threads_initialised3
!= num_threads
) MB();
327 extern void check_tree(set_t
*);
328 check_tree(shared
.set
);
332 if ( id
== num_threads
- 1 )
334 gettimeofday(&done_time
, NULL
);
337 _destroy_gc_subsystem();
345 #define THREAD_TEST thread_start
346 #define THREAD_FLAGS THR_BOUND
349 static pthread_attr_t attr
;
352 static void test_multithreaded (void)
355 pthread_t thrs
[MAX_THREADS
];
357 int min_successes
, max_successes
;
358 int ticksps
= sysconf(_SC_CLK_TCK
);
359 float wall_time
, user_time
, sys_time
;
361 if ( num_threads
== 1 ) goto skip_thread_creation
;
364 i
= pthread_attr_init (&attr
);
366 fprintf (stderr
, "URK! pthread_attr_init rc=%d\n", i
);
368 i
= pthread_attr_setscope (&attr
, PTHREAD_SCOPE_SYSTEM
);
370 fprintf (stderr
, "URK! pthread_attr_setscope rc=%d\n", i
);
375 pthread_setconcurrency(num_threads
+ 1);
377 pthread_setconcurrency(num_threads
);
380 for (i
= 0; i
< num_threads
; i
++)
384 pthread_create (&thrs
[i
], &attr
, THREAD_TEST
, (void *)i
);
386 pthread_create (&thrs
[i
], NULL
, THREAD_TEST
, (void *)i
);
390 skip_thread_creation
:
391 if ( num_threads
== 1 )
397 for (i
= 0; i
< num_threads
; i
++)
399 (void)pthread_join (thrs
[i
], NULL
);
403 wall_time
= (float)(TVAL(done_time
) - TVAL(start_time
))/ 1000000;
404 user_time
= ((float)(done_tms
.tms_utime
- start_tms
.tms_utime
))/ticksps
;
405 sys_time
= ((float)(done_tms
.tms_stime
- start_tms
.tms_stime
))/ticksps
;
407 log_float ("wall_time_s", wall_time
);
408 log_float ("user_time_s", user_time
);
409 log_float ("system_time_s", sys_time
);
412 min_successes
= INT_MAX
;
413 max_successes
= INT_MIN
;
414 for ( i
= 0; i
< num_threads
; i
++ )
416 num_successes
+= successes
[i
];
417 if ( successes
[i
] < min_successes
) min_successes
= successes
[i
];
418 if ( successes
[i
] > max_successes
) max_successes
= successes
[i
];
421 log_int ("min_successes", min_successes
);
422 log_int ("max_successes", max_successes
);
423 log_int ("num_successes", num_successes
);
425 log_float("us_per_success", (num_threads
*wall_time
*1000000.0)/num_successes
);
427 log_int("log max key", log_max_key
);
431 static void tstp_handler(int sig
, siginfo_t
*info
, ucontext_t
*uc
)
433 static unsigned int sem
= 0;
434 unsigned long *esp
= (unsigned long *)(uc
->uc_mcontext
.gregs
[7]);
437 while ( CASIO(&sem
, 0, 1) != 0 ) sched_yield();
439 printf("Signal %d for pid %d\n", sig
, pid
);
440 printf("%d: EIP=%08x EAX=%08x EBX=%08x ECX=%08x EDX=%08x\n", pid
,
441 uc
->uc_mcontext
.gregs
[14], uc
->uc_mcontext
.gregs
[11],
442 uc
->uc_mcontext
.gregs
[ 8], uc
->uc_mcontext
.gregs
[10],
443 uc
->uc_mcontext
.gregs
[ 9]);
444 printf("%d: ESP=%08x EBP=%08x ESI=%08x EDI=%08x EFL=%08x\n", pid
,
445 uc
->uc_mcontext
.gregs
[ 7], uc
->uc_mcontext
.gregs
[ 6],
446 uc
->uc_mcontext
.gregs
[ 5], uc
->uc_mcontext
.gregs
[ 4],
447 uc
->uc_mcontext
.gregs
[16]);
452 for ( ; ; ) sched_yield();
456 int main (int argc
, char **argv
)
460 unsigned long log_header
[] = { 0, MAX_ITERATIONS
, 0 };
464 printf("%s <num_threads> <read_proportion> <key power> <log name>\n"
465 "(0 <= read_proportion <= 256)\n", argv
[0]);
471 printf("%s <num_threads> <read_proportion> <key power>\n"
472 "(0 <= read_proportion <= 256)\n", argv
[0]);
477 memset(&shared
, 0, sizeof(shared
));
479 num_threads
= atoi(argv
[1]);
480 log_int ("num_threads", num_threads
);
482 proportion
= atoi(argv
[2]);
483 log_float ("frac_reads", (float)proportion
/256.0);
485 log_max_key
= atoi(argv
[3]);
486 max_key
= 1 << atoi(argv
[3]);
487 log_int("max_key", max_key
);
489 log_int ("max_iterations", MAX_ITERATIONS
);
491 log_int ("wall_time_limit_s", MAX_WALL_TIME
);
495 int st
, maxcpu
= sysconf(_SC_CPUID_MAX
), i
, j
=0;
497 /* Favour processors that don't handle I/O interrupts. */
498 for ( i
= 0; i
<= maxcpu
; i
++ )
500 st
= p_online(i
, P_STATUS
);
501 if ( st
== P_NOINTR
)
503 if ( j
== num_threads
) break;
505 if ( j
== num_threads
) break;
509 /* Fall back to the system quads if necessary. */
510 for ( i
= 0; i
<= maxcpu
; i
++ )
512 st
= p_online(i
, P_STATUS
);
513 if ( st
== P_ONLINE
)
515 if ( j
== num_threads
) break;
517 if ( j
== num_threads
) break;
521 if ( j
!= num_threads
)
523 printf("Urk! Not enough CPUs for threads (%d < %d)\n",
531 log_header
[0] = num_threads
;
532 log_header
[2] = max_key
;
533 global_log
= malloc(SIZEOF_GLOBAL_LOG
);
538 struct sigaction act
;
539 memset(&act
, 0, sizeof(act
));
540 act
.sa_handler
= (void *)tstp_handler
;
541 act
.sa_flags
= SA_SIGINFO
;
542 sigaction(SIGTSTP
, &act
, NULL
);
543 sigaction(SIGQUIT
, &act
, NULL
);
544 sigaction(SIGSEGV
, &act
, NULL
);
548 test_multithreaded ();
553 printf("Writing log...\n");
554 /* Write logs to data file */
555 fd
= open(argv
[4], O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
558 fprintf(stderr
, "Error writing log!\n");
562 if ( (write(fd
, log_header
, sizeof(log_header
)) != sizeof(log_header
)) ||
563 (write(fd
, global_log
, SIZEOF_GLOBAL_LOG
) != SIZEOF_GLOBAL_LOG
) )
565 fprintf(stderr
, "Log write truncated or erroneous\n");