1 /* Copyright (C) 2021 Free Software Foundation, Inc.
4 This file is part of GNU Binutils.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
21 /* mttest -- show threaded use of global and local locks */
23 #include <sys/types.h>
28 #include <sys/sysinfo.h>
29 #include <sys/procfs.h>
30 #include <sys/fcntl.h>
41 #include <semaphore.h>
45 #include <linux/sched.h>
48 #include <sys/syscall.h>
50 #include <linux/futex.h>
51 #include <linux/unistd.h>
52 static int CLONE_FLAGS
[] = {
53 CLONE_VM
| CLONE_FS
| CLONE_FILES
| CLONE_SIGHAND
| CLONE_SYSVSEM
| SIGCHLD
| CLONE_CHILD_CLEARTID
| CLONE_PARENT_SETTID
| CLONE_IO
,
54 CLONE_VM
| SIGCHLD
| CLONE_CHILD_CLEARTID
| CLONE_PARENT_SETTID
,
55 CLONE_VM
| CLONE_FS
| CLONE_FILES
| CLONE_SIGHAND
| CLONE_THREAD
| CLONE_SYSVSEM
| SIGCHLD
| CLONE_CHILD_CLEARTID
| CLONE_PARENT_SETTID
| CLONE_IO
,
56 CLONE_VM
| CLONE_SIGHAND
| CLONE_THREAD
| SIGCHLD
| CLONE_CHILD_CLEARTID
| CLONE_PARENT_SETTID
59 #define CLONE_STACK_SIZE 8388608
60 #define CLONE_TLS_SIZE 4096
61 #define CLONE_RED_SIZE 4096
65 typedef int processorid_t
;
66 typedef long long hrtime_t
;
67 typedef struct timespec timespec_t
;
68 extern hrtime_t
gethrtime ();
69 extern hrtime_t
gethrvtime ();
71 timespec_t
* hrt_to_ts (hrtime_t hrt
);
72 static const pthread_mutex_t mutex_initializer
= PTHREAD_MUTEX_INITIALIZER
;
74 #define CLONE_IO 0x80000000 /* Clone io context */
75 char *model
= "Cloned threads";
78 char *model
= "Bound Posix threads";
80 char *model
= "Unbound Posix threads";
84 char *prtime (time_t *);
85 int get_clock_rate (void);
89 void start_prof (void);
90 void finish_prof (void);
93 #define _STRUCTURED_PROC 1
96 #define NUM_OF_THREADS 4
97 #define NUM_OF_BLOCKS 4
98 #define NUM_OF_RESOURCES 3
99 #define MYTIMEOUT 1000000000
100 #define MYDBLTIMEOUT ((double) 1000000000.)
102 int repeat_count
= 1; /* number of times to repeat test */
103 int job_index
= -1; /* index of selected job, if just one */
104 int uniprocessor
= 0; /* non-zero if -u specified; causes single processor bind */
106 processorid_t ocpuid
;
108 // not a typedef; simplifies analyzer data display output
109 #define workCtr_t double
111 typedef struct workStruct_t
118 typedef struct Workblk
120 int index
; /* index of this block */
121 int strategy
; /* specifies type of locking to do */
122 int proffail
; /* flag set if thread loses interrupts */
124 pid_t tid
; /* Linux kernel thread id */
126 pthread_t tid
; /* thread processing buffer */
128 pthread_mutex_t lock
; /* lock for this buffer */
129 lwpid_t ilwpid
; /* lwp processing buffer (initially) */
130 lwpid_t lwpid
; /* lwp processing buffer (after sync) */
133 hrtime_t start
; /* buffer fetched, wall clock */
134 hrtime_t vstart
; /* buffer fetched, CPU timer */
135 hrtime_t ready
; /* lock acquired (if needed), wall clock */
136 hrtime_t vready
; /* lock acquired (if needed), CPU timer */
137 hrtime_t done
; /* work done, wall clock */
138 hrtime_t vdone
; /* work done, CPU timer */
139 hrtime_t compute_ready
; /* compute ready, wall clock */
140 hrtime_t compute_vready
; /* compute ready, CPU timer */
141 hrtime_t compute_done
; /* compute done, wall clock */
142 hrtime_t compute_vdone
; /* compute done, CPU timer */
143 struct Workblk
*next
; /* for queue management */
144 workStruct_t list
[100];
147 /* lookup table for behavior scripts */
151 void (*test_func
)(Workblk
*, struct scripttab
*);
153 void (*called_func
)(workStruct_t
*);
157 void resolve_symbols ();
158 void init_micro_acct ();
159 void compute_set (volatile workStruct_t
*x
);
160 void compute (workStruct_t
*x
);
161 void computeA (workStruct_t
*x
);
162 void computeB (workStruct_t
*x
);
163 void computeC (workStruct_t
*x
);
164 void computeD (workStruct_t
*x
);
165 void computeE (workStruct_t
*x
);
166 void computeF (workStruct_t
*x
);
167 void computeG (workStruct_t
*x
);
168 void computeH (workStruct_t
*x
);
169 void computeI (workStruct_t
*x
);
170 void computeJ (workStruct_t
*x
);
171 void computeK (workStruct_t
*x
);
172 void addone (workCtr_t
*x
);
173 void init_arrays (int strat
);
175 void *do_work (void *v
);
177 void nothreads (Workblk
*array
, struct scripttab
*k
);
178 void lock_none (Workblk
*array
, struct scripttab
*k
);
179 void cache_trash (Workblk
*array
, struct scripttab
*k
);
180 void lock_global (Workblk
*array
, struct scripttab
*k
);
181 void trylock_global (Workblk
*array
, struct scripttab
*k
);
182 void lock_local (Workblk
*array
, struct scripttab
*k
);
183 void calladd (Workblk
*array
, struct scripttab
*k
);
184 void cond_global (Workblk
*array
, struct scripttab
*k
);
185 void cond_timeout_global (Workblk
*array
, struct scripttab
*k
);
186 void sema_global (Workblk
*array
, struct scripttab
*k
);
187 void read_write (Workblk
*array
, struct scripttab
*k
);
188 void s5sem (Workblk
*array
, struct scripttab
*k
);
189 FILE *open_output (char *filename
);
190 int close_file (FILE *f
);
191 void scale_init (int argcc
, char **argvv
);
195 struct scripttab scripttab
[] = {
197 {"nothreads", nothreads
, "compute", compute
},
198 {"lock_none", lock_none
, "computeA", computeA
},
199 {"cache_trash", cache_trash
, "computeB", computeB
},
200 {"calladd", calladd
, "computeF", computeF
},
201 {"sema_global", sema_global
, "computeI", computeI
},
203 {"nothreads", nothreads
, "compute", compute
},
204 {"cond_timeout_global", cond_timeout_global
, "computeH", computeH
},
205 {"lock_none", lock_none
, "computeA", computeA
},
206 {"cache_trash", cache_trash
, "computeB", computeB
},
207 {"lock_global", lock_global
, "computeC", computeC
},
208 {"trylock_global", trylock_global
, "computeD", computeD
},
209 {"lock_local", lock_local
, "computeE", computeE
},
210 {"calladd", calladd
, "computeF", computeF
},
211 {"sema_global", sema_global
, "computeI", computeI
},
212 {"cond_global", cond_global
, "computeG", computeG
},
214 {NULL
, NULL
, NULL
, NULL
}
217 static pthread_mutex_t global_lock
= PTHREAD_MUTEX_INITIALIZER
;
218 static pthread_mutex_t global_cond_lock
= PTHREAD_MUTEX_INITIALIZER
;
219 static pthread_mutex_t global_cond_lock2
= PTHREAD_MUTEX_INITIALIZER
;
220 static pthread_cond_t global_cond
= PTHREAD_COND_INITIALIZER
;
221 static timespec_t time_out
;
222 static sem_t global_sema_lock
; /* dynamically initted */
223 static int s5_sema_id
;
224 static int global_cond_flag
= TRUE
;
225 static int count
= NUM_OF_RESOURCES
;
227 /* an array of workStruct_ts that is contiguous */
228 workStruct_t
*element
;
236 int nthreads
= NUM_OF_THREADS
;
237 int narrays
= NUM_OF_BLOCKS
;
243 static sem_t fetch_sema_lock
;
245 static void *stack_space
[NUM_OF_THREADS
];
246 static void *stack
[NUM_OF_THREADS
];
247 int stack_size
= CLONE_STACK_SIZE
;
249 static pthread_t
*tid
;
254 main (int argc
, char **argv
, char **envp
)
257 scale_init (argc
, argv
);
259 #define ALIGNMENTOFFSET 2 /* adjust alignment */
260 i
= sizeof (workStruct_t
) * (narrays
+ ALIGNMENTOFFSET
);
261 element
= memalign (64, i
);
264 perror ("calloc( narrays, sizeof(workStruct_t) )");
267 compute_set (element
);
268 memset (element
, 0, i
);
269 element
+= ALIGNMENTOFFSET
;
274 fid
= open_output ("mttest.acct");
276 i
= (sizeof (scripttab
) / sizeof ( struct scripttab
) - 1);
279 fprintf (fid
, "Number of tests: %d Repeat count: %d\n", i
, repeat_count
);
280 fprintf (fid
, "MHz: %d\n", get_clock_rate ());
281 fprintf (fid
, "X Incl. Total Incl. CPU Incl. Sync. Wait Name (%s)\n",
283 fprintf (fid
, "X %7.3f %7.3f %7.3f %s\n",
284 0.0, 0.0, 0.0, "<Unknown>");
286 name
= strdup (argv
[0]);
288 pthread_attr_init (&attr
);
291 pthread_attr_setscope (&attr
, PTHREAD_SCOPE_SYSTEM
);
293 sem_init (&global_sema_lock
, 0, count
);
295 sem_init (&fetch_sema_lock
, 0, 1);
296 for (i
= 0; i
< nthreads
; i
++)
298 stack_space
[i
] = mmap (NULL
, stack_size
, PROT_READ
| PROT_WRITE
299 | PROT_EXEC
, MAP_PRIVATE
| MAP_ANONYMOUS
| MAP_GROWSDOWN
, -1, 0);
300 if ((void*) - 1 == stack_space
[i
])
302 fprintf (stderr
, "Error: mmap returned -1\n");
305 mprotect (stack_space
[i
], CLONE_RED_SIZE
, PROT_NONE
);
306 stack
[i
] = (char*) (stack_space
[i
]) + stack_size
- CLONE_TLS_SIZE
; // stack grows back
320 Workblk
*in_queue
= NULL
;
321 Workblk
*in_queue_last
= NULL
;
323 pthread_mutex_t queue_lock
;
326 queue_work (Workblk
* w
)
328 if (in_queue
== NULL
)
335 in_queue_last
->next
= w
;
343 /* acquire the queue lock */
345 sem_wait (&fetch_sema_lock
);
347 pthread_mutex_lock (&queue_lock
);
350 /* get the next block */
351 Workblk
*w
= in_queue
;
356 if (in_queue
== NULL
)
357 in_queue_last
= NULL
;
360 sem_post (&fetch_sema_lock
);
362 pthread_mutex_unlock (&queue_lock
);
365 /* return the block */
379 struct timeval ttime
;
383 head
.arrays
= (Workblk
*) calloc (narrays
, sizeof (Workblk
));
385 for (i
= 0, array
= head
.arrays
; i
< narrays
; i
++, array
++)
388 printf ("%s: number of %s = %d, number of blocks = %d, repeat %d times %s\n",
389 name
, model
, nthreads
, narrays
, repeat_count
,
390 (uniprocessor
== 0 ? "" : "[single CPU]"));
392 tid
= (pid_t
*) calloc (nthreads
*repeat_count
, sizeof (pid_t
));
394 tid
= (pthread_t
*) calloc (nthreads
*repeat_count
, sizeof (pthread_t
));
396 for (count
= 0; count
< repeat_count
; count
++)
398 (void) gettimeofday (&ttime
, NULL
);
399 secs
= (time_t) ttime
.tv_sec
;
400 printf ("Iteration %d, starting %s\n", count
+ 1, prtime (&secs
));
406 if (k
->test_name
== NULL
)
409 printf ("begin thread_work, %s\n", k
->test_name
);
411 start
= gethrtime ();
412 vstart
= gethrvtime ();
414 if (strcmp (k
->test_name
, "nothreads") == 0)
416 /* the "nothreads" task is special-cased to run in the main thread */
418 do_work (&one_thread
);
420 else if (nthreads
== 1)
423 do_work (&one_thread
);
428 vend
= gethrvtime ();
429 dump_arrays (end
- start
, vend
- vstart
, i
);
434 k
= &scripttab
[job_index
];
435 if (k
->test_name
== NULL
)
438 printf ("begin thread_work, %s\n", k
->test_name
);
439 init_arrays (job_index
);
440 start
= gethrtime ();
441 vstart
= gethrvtime ();
442 if (strcmp (k
->test_name
, "nothreads") == 0)
444 /* first one is special-cased to run in 1 thread */
446 do_work (&one_thread
);
448 else if (nthreads
== 1)
453 vend
= gethrvtime ();
454 dump_arrays (end
- start
, vend
- vstart
, job_index
);
458 /* we're done, return */
463 init_arrays (int strat
)
467 for (i
= 0, array
= head
.arrays
; i
< narrays
; i
++, array
++)
469 bzero (array
, sizeof (Workblk
));
471 array
->strategy
= strat
;
477 dump_arrays (hrtime_t real
, hrtime_t cpu
, int case_index
)
480 double t1
, t2
, t3
, t4
, t5
, t6
, t7
, t8
;
483 double sumtotal
= 0.;
486 double sumCompTotal
= 0.;
487 double sumCompCPU
= 0.;
489 printf (" real real real CPU\n");
490 printf ("idx (t id) total lock crunch crunch\n");
491 for (i
= 0, array
= head
.arrays
; i
< narrays
; i
++, array
++)
493 /* check to see if data lost for this block */
494 /* set flag to disable the comparison */
495 /* convert times to seconds */
496 t1
= ((double) array
->done
- array
->start
) / MYDBLTIMEOUT
;
497 t2
= ((double) array
->vdone
- array
->vstart
) / MYDBLTIMEOUT
;
498 t3
= ((double) array
->ready
- array
->start
) / MYDBLTIMEOUT
;
499 t4
= ((double) array
->vready
- array
->vstart
) / MYDBLTIMEOUT
;
500 t5
= ((double) array
->done
- array
->ready
) / MYDBLTIMEOUT
;
501 t6
= ((double) array
->vdone
- array
->vready
) / MYDBLTIMEOUT
;
502 t7
= ((double) array
->compute_done
- array
->compute_ready
) / MYDBLTIMEOUT
;
503 t8
= ((double) array
->compute_vdone
- array
->compute_vready
)
506 if (array
->proffail
!= 0)
508 sumtotal
= sumtotal
+ t1
; /* incl. total time */
509 sumlock
= sumlock
+ t3
; /* incl. sync. wait time */
512 * for bound threads, sumCPU includes the synchronization
513 * CPU time; for unbound it does not
515 sumCPU
= sumCPU
+ t2
; /* test incl. CPU time */
517 sumCPU
= sumCPU
+ t6
; /* test incl. CPU time */
519 sumCompTotal
= sumCompTotal
+ t7
; /* compute incl. totaltime */
520 sumCompCPU
= sumCompCPU
+ t8
; /* compute incl. CPU time */
521 printf ("#%2d (t%3ld, il%3d, l%3d) %10.6f %10.6f %10.6f %10.6f%s\n",
522 array
->index
, array
->tid
, array
->ilwpid
, array
->lwpid
, t1
, t3
,
523 t5
, t6
, array
->proffail
== 0 ? "" : " *");
524 if (t4
== 0) printf ("t4 == 0\n");
525 assert (array
->lwpid
> 0);
527 assert (array
->lwpid
== array
->ilwpid
);
531 k
= &scripttab
[case_index
];
533 printf ("%-25s %10.6f %10.6f %-9s %10.6f\n", k
->test_name
, sumtotal
,
534 sumlock
, k
->called_name
, sumCPU
);
535 printf ("main %10.6f\n\n",
536 (double) real
/ MYDBLTIMEOUT
);
538 /* write accounting record for task */
539 fprintf (fid
, "X %7.3f %7.3f %7.3f %s%s\n",
540 sumtotal
, sumCPU
, sumlock
, k
->test_name
,
541 (proffail
== 0 ? "" : " *"));
542 /* write accounting record for task's compute function */
543 fprintf (fid
, "X %7.3f %7.3f 0. %s%s\n",
544 sumCompTotal
, sumCompCPU
, k
->called_name
,
545 (proffail
== 0 ? "" : " *"));
556 pid_t ctid
[NUM_OF_THREADS
];
557 for (i
= 0; i
< nthreads
; i
++)
561 /* create nthreads threads, having each start at do_work */
562 for (i
= 0; i
< nthreads
; i
++)
566 retval
= pthread_create (&(tid
[i
]), &attr
, do_work
, 0);
569 retval
= pthread_create (&(tid
[i
]), 0, do_work
, 0);
572 tid
[i
] = retval
= clone ((int (*)(void*))do_work
, stack
[i
],
573 CLONE_FLAGS
[i
% sizeof (CLONE_FLAGS
)], NULL
,
574 &(ctid
[i
]), NULL
, &(ctid
[i
]));
577 perror ("Oops, clone failed");
583 perror ("Oops, thr_create failed");
589 /* wait for all threads to complete their work and join */
590 for (i
= 0; i
< nthreads
; i
++)
594 while (ctid
[i
] == -1)
597 syscall (__NR_futex
, &(ctid
[i
]), FUTEX_WAIT
, tid
[i
], NULL
);
599 pthread_join (tid
[i
], 0);
603 for (i
= 0; i
< nthreads
/ 2; i
++)
606 waitpid (tid
[i
], &status
, __WALL
);
611 /* do_work: process array's data with locking, based on array->strategy */
621 pid_t mytid
= syscall (__NR_gettid
);
623 pthread_t mytid
= pthread_self ();
626 /* delay to ensure that a tick passes, so that the
627 * first profile packet doesn't show the thread startup time
628 * attributed to the accounting functions
631 for (i
= 0; i
< 2000000; i
++)
636 /* fetch a workblk */
637 array
= fetch_work ();
638 if (array
== NULL
) /* we're done */
640 array
->lock
= mutex_initializer
;
643 array
->ilwpid
= getpid () /* pthread_self()*/;
645 array
->lwpid
= -1; /* initialize to inappropriate value */
646 array
->start
= gethrtime ();
647 array
->vstart
= gethrvtime ();
649 k
= &scripttab
[array
->strategy
];
650 (k
->test_func
)(array
, k
);
652 array
->done
= gethrtime ();
653 array
->vdone
= gethrvtime ();
654 array
->lwpid
= getpid () /* pthread_self()*/;
657 assert (array
->lwpid
== array
->ilwpid
);
668 /* nothreads: process array's data with no locking; called without threads */
670 nothreads (Workblk
*array
, struct scripttab
*k
)
672 array
->ready
= gethrtime ();
673 array
->vready
= gethrvtime ();
674 array
->compute_ready
= array
->ready
;
675 array
->compute_vready
= array
->vready
;
677 /* do some work on the current array */
678 (k
->called_func
)(&array
->list
[0]);
680 array
->compute_done
= gethrtime ();
681 array
->compute_vdone
= gethrvtime ();
685 /* lock_none: process array's data with no locking */
687 lock_none (Workblk
*array
, struct scripttab
*k
)
689 array
->ready
= array
->start
;
690 array
->vready
= array
->vstart
;
691 array
->compute_ready
= array
->ready
;
692 array
->compute_vready
= array
->vready
;
694 /* do some work on the current array */
695 (k
->called_func
)(&array
->list
[0]);
697 array
->compute_done
= gethrtime ();
698 array
->compute_vdone
= gethrvtime ();
703 * called for even numbered l1 cache lines
706 cache_trash_even (Workblk
*array
, struct scripttab
*k
)
708 /* use a datum that will share a cache line with others */
709 (k
->called_func
)(&element
[array
->index
]);
713 * called for odd numbered l1 cache lines
716 cache_trash_odd (Workblk
*array
, struct scripttab
*k
)
718 /* use a datum that will share a cache line with others */
719 (k
->called_func
)(&element
[array
->index
]);
722 /* cache_trash: multiple threads refer to adjacent words,
723 * causing false sharing of cache lines, and trashing
726 cache_trash (Workblk
*array
, struct scripttab
*k
)
728 array
->ready
= array
->start
;
729 array
->vready
= array
->vstart
;
730 array
->compute_ready
= array
->ready
;
731 array
->compute_vready
= array
->vready
;
733 /* use a datum that will share a cache line with others */
734 if ((unsigned long) (&element
[array
->index
]) / 32 & 1)
735 cache_trash_odd (array
, k
);
737 cache_trash_even (array
, k
);
739 array
->compute_done
= gethrtime ();
740 array
->compute_vdone
= gethrvtime ();
743 /* lock_global: use a global lock to process array's data */
745 lock_global (Workblk
*array
, struct scripttab
*k
)
747 /* acquire the global lock */
748 pthread_mutex_lock (&global_lock
);
750 array
->ready
= gethrtime ();
751 array
->vready
= gethrvtime ();
752 array
->compute_ready
= array
->ready
;
753 array
->compute_vready
= array
->vready
;
755 /* do some work on the current array */
756 (k
->called_func
)(&array
->list
[0]);
758 array
->compute_done
= gethrtime ();
759 array
->compute_vdone
= gethrvtime ();
761 /* free the global lock */
762 pthread_mutex_unlock (&global_lock
);
763 /* make another call to preclude tail-call optimization on the unlock */
767 /* trylock_global: busy-wait on a global lock to process array's data */
769 trylock_global (Workblk
*array
, struct scripttab
*k
)
773 /* set ready before starting, since this is a busy wait */
774 array
->ready
= gethrtime ();
775 array
->vready
= gethrvtime ();
777 /* busy wait to acquire the global lock */
780 ret
= pthread_mutex_trylock (&global_lock
);
782 while (ret
== EBUSY
);
783 array
->compute_ready
= gethrtime ();
784 array
->compute_vready
= gethrvtime ();
786 /* do some work on the current array */
787 (k
->called_func
)(&array
->list
[0]);
789 array
->compute_done
= gethrtime ();
790 array
->compute_vdone
= gethrvtime ();
792 /* free the global lock */
793 pthread_mutex_unlock (&global_lock
);
794 /* make another call to preclude tail-call optimization on the unlock */
798 /* lock_local: use a local lock to process array's data */
800 lock_local (Workblk
*array
, struct scripttab
*k
)
802 /* acquire the local lock */
803 pthread_mutex_lock (&(array
->lock
));
804 array
->ready
= gethrtime ();
805 array
->vready
= gethrvtime ();
806 array
->compute_ready
= array
->ready
;
807 array
->compute_vready
= array
->vready
;
809 /* do some work on the current array */
810 (k
->called_func
)(&array
->list
[0]);
812 array
->compute_done
= gethrtime ();
813 array
->compute_vdone
= gethrvtime ();
815 /* free the local lock */
816 pthread_mutex_unlock (&array
->lock
);
817 /* make another call to preclude tail-call optimization on the unlock */
821 /* cond_global: use a global condition variable to process array's data */
823 cond_global (Workblk
*array
, struct scripttab
*k
)
825 /* acquire the global condition lock */
826 pthread_mutex_lock (&global_cond_lock
);
828 /* check to see if the condition flag is true, If not then wait
829 for that condition flag to become true. */
830 while (global_cond_flag
!= TRUE
)
831 pthread_cond_wait (&global_cond
, &global_cond_lock
);
832 /* Now, condition is true, and we have the global_cond_lock */
834 /* set the condition flag to be FALSE, so when a new thread
835 * is created, it should wait till this one is done.
837 global_cond_flag
= FALSE
;
839 /* free the global_cond_lock and acquire the global lock */
840 pthread_mutex_unlock (&global_cond_lock
);
841 pthread_mutex_lock (&global_lock
);
843 array
->ready
= gethrtime ();
844 array
->vready
= gethrvtime ();
846 array
->compute_ready
= array
->ready
;
847 array
->compute_vready
= array
->vready
;
849 /* do some work on the current array */
850 (k
->called_func
)(&array
->list
[0]);
852 array
->compute_done
= gethrtime ();
853 array
->compute_vdone
= gethrvtime ();
855 /* free the global lock */
856 pthread_mutex_unlock (&global_lock
);
858 /* now set the condition, and signal any other threads */
859 pthread_mutex_lock (&global_cond_lock
);
861 global_cond_flag
= TRUE
;
862 pthread_cond_signal (&global_cond
);
863 pthread_mutex_unlock (&global_cond_lock
);
864 /* make another call to preclude tail-call optimization on the unlock */
868 /* cond_timeout_global: use a global condition time wait variable to
869 process array's data */
871 cond_timeout_global (Workblk
*array
, struct scripttab
*k
)
874 struct timeval current_time
;
876 /* acquire the global condition lock */
877 pthread_mutex_lock (&global_cond_lock
);
878 gettimeofday (¤t_time
, NULL
);
879 time_out
.tv_sec
= current_time
.tv_sec
;
880 time_out
.tv_nsec
= current_time
.tv_usec
* 1000;
882 /* check to see if the condition flag is true, If not then wait
883 * for that condition flag to become true
886 while (global_cond_flag
!= TRUE
)
888 /* add MYTIMEOUT to current time for timeout */
889 time_out
.tv_nsec
+= MYTIMEOUT
;
890 while (time_out
.tv_nsec
> 1000000000)
892 time_out
.tv_nsec
-= 1000000000;
895 err
= pthread_cond_timedwait (&global_cond
, &global_cond_lock
, &time_out
);
899 /* Now, condition is true, and we have the global_cond_lock */
901 pthread_mutex_unlock (&global_cond_lock
);
903 pthread_mutex_lock (&global_cond_lock2
);
904 global_cond_flag
= FALSE
;
905 pthread_mutex_unlock (&global_cond_lock2
);
907 /* acquire the global lock */
908 pthread_mutex_lock (&global_lock
);
910 array
->ready
= gethrtime ();
911 array
->vready
= gethrvtime ();
913 array
->compute_ready
= array
->ready
;
914 array
->compute_vready
= array
->vready
;
916 /* do some work on the current array */
917 (k
->called_func
)(&array
->list
[0]);
918 array
->compute_done
= gethrtime ();
919 array
->compute_vdone
= gethrvtime ();
921 /* free the global lock */
922 pthread_mutex_unlock (&global_lock
);
924 /* now set the condition, and signal any other threads */
925 pthread_mutex_lock (&global_cond_lock2
);
927 global_cond_flag
= TRUE
;
928 pthread_cond_signal (&global_cond
);
929 pthread_mutex_unlock (&global_cond_lock2
);
931 /* make another call to preclude tail-call optimization on the unlock */
935 /* read_write: use a global Reader/Writer lock to process array's data */
937 read_write (Workblk
*array
, struct scripttab
*k
)
939 /* make another call to preclude tail-call optimization on the unlock */
943 /* sema_global: use a global semaphore to process array's data */
945 sema_global (Workblk
*array
, struct scripttab
*k
)
947 sem_wait (&global_sema_lock
);
948 array
->ready
= gethrtime ();
949 array
->vready
= gethrvtime ();
950 array
->compute_ready
= array
->ready
;
951 array
->compute_vready
= array
->vready
;
953 /* do some work on the current array */
954 (k
->called_func
)(&array
->list
[0]);
956 array
->compute_done
= gethrtime ();
957 array
->compute_vdone
= gethrvtime ();
958 sem_post (&global_sema_lock
);
960 /* make another call to preclude tail-call optimization on the unlock */
964 /* s5sema: use a global UNIX System V semaphore to process array's data */
966 s5sem (Workblk
*array
, struct scripttab
*k
)
968 static struct sembuf op_wait
[] = {
971 static struct sembuf op_post
[] = {
976 /* set ready before starting, since this is a busy wait */
977 array
->ready
= gethrtime ();
978 array
->vready
= gethrvtime ();
981 sema_val
= semop (s5_sema_id
, op_wait
, 1);
983 while (sema_val
== -1);
985 array
->compute_ready
= gethrtime ();
986 array
->compute_vready
= gethrvtime ();
988 /* do some work on the current array */
989 (k
->called_func
)(&array
->list
[0]);
991 array
->compute_done
= gethrtime ();
992 array
->compute_vdone
= gethrvtime ();
994 if (semop (s5_sema_id
, op_post
, 1) == -1)
995 perror ("semop: post");
996 /* make another call to preclude tail-call optimization on the unlock */
1000 /* lock_local: use a local lock to process array's data */
1002 calladd (Workblk
*array
, struct scripttab
*k
)
1004 array
->ready
= array
->start
;
1005 array
->vready
= array
->vstart
;
1006 array
->compute_ready
= array
->ready
;
1007 array
->compute_vready
= array
->vready
;
1008 (k
->called_func
)(&array
->list
[0]);
1009 array
->compute_done
= gethrtime ();
1010 array
->compute_vdone
= gethrvtime ();
1013 /* compute*: several copies, each burns cpu time, incrementing a workStruct_t */
1014 static long long loop_count
= 80000000;
1017 compute_set (volatile workStruct_t
*x
)
1019 double testtime
= 3.0;
1020 char *s
= getenv ("SP_COLLECTOR_TEST_TIMER");
1023 testtime
= atof (s
);
1027 hrtime_t t
= gethrtime ();
1030 for (long long i
= 0; i
< loop_count
; i
++)
1031 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1032 t
= gethrtime () - t
;
1033 loop_count
*= testtime
* 1e9
/ t
;
1034 printf ("compute_set: loop_count=%lld\n", loop_count
);
1038 compute (workStruct_t
*x
)
1041 for (long long i
= 0; i
< loop_count
; i
++)
1042 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1046 computeA (workStruct_t
*x
)
1049 for (long long i
= 0; i
< loop_count
; i
++)
1050 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1054 computeB (workStruct_t
*x
)
1057 for (long long i
= 0; i
< loop_count
; i
++)
1058 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1062 computeC (workStruct_t
*x
)
1065 for (long long i
= 0; i
< loop_count
; i
++)
1066 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1070 computeD (workStruct_t
*x
)
1073 for (long long i
= 0; i
< loop_count
; i
++)
1074 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1078 computeE (workStruct_t
*x
)
1081 for (long long i
= 0; i
< loop_count
; i
++)
1082 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1085 /* note that this one is different from the others, in that it calls
1086 * a function to do the add
1089 computeF (workStruct_t
*x
)
1092 for (long long i
= 0; i
< loop_count
; i
++)
1093 addone (&x
->sum_ctr
);
1097 computeG (workStruct_t
*x
)
1100 for (long long i
= 0; i
< loop_count
; i
++)
1101 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1105 computeH (workStruct_t
*x
)
1108 for (long long i
= 0; i
< loop_count
; i
++)
1109 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1113 computeI (workStruct_t
*x
)
1116 for (long long i
= 0; i
< loop_count
; i
++)
1117 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1121 computeJ (workStruct_t
*x
)
1124 for (long long i
= 0; i
< loop_count
; i
++)
1125 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1129 computeK (workStruct_t
*x
)
1132 for (long long i
= 0; i
< loop_count
; i
++)
1133 x
->sum_ctr
= x
->sum_ctr
+ 1.0;
1137 addone (workCtr_t
*x
)
1143 open_output (char *filename
)
1146 FILE *f
= fopen (filename
, "w");
1148 fprintf (stderr
, "Open of %s for output failed: %s\n",
1149 filename
, strerror (errno
));
1154 close_file (FILE *f
)
1161 perror ("Close failed");
1166 scale_init (int argcc
, char **argvv
)
1171 struct scripttab
*kk
;
1173 if (argcc
>= 2) /* run mttest with options */
1175 for (int i
= 1; i
< argcc
; i
++)
1178 if (argvv
[i
][0] != '-')
1180 if (argvv
[i
][1] == 'h' || argvv
[i
][1] == 'H')
1182 if (argvv
[i
][1] == 'u')
1187 if (strlen (argvv
[i
]) == 2)
1189 /* argument has blank separating key and number */
1201 /* argument has no blank separating key and number */
1206 switch (argvv
[i
][1])
1222 /* argument is a job name; p points to string */
1225 kk
= &scripttab
[ii
];
1226 if (kk
->test_name
== NULL
) /* Oops, name not found */
1228 if (strcmp (kk
->test_name
, p
) == 0) /* found it */
1242 Print_Usage (int error
)
1245 printf ("\nError: Incorrect option\n");
1246 else if (error
== 2)
1247 printf ("\nError: job name not found\n");
1248 printf ("Usage: mttest [-t num_of_threads] [-b num_of_blocks] "
1249 "[-R repeat_count] [-u] [-j job_name]\n");
1250 printf (" -u implies binding all LWPs to one CPU with processor_bind\n");
1251 printf (" job_name is one of:\n");
1252 for (int ii
= 0;; ii
++)
1254 struct scripttab
*kk
= &scripttab
[ii
];
1255 if (kk
->test_name
== NULL
)
1257 printf ("\t%s\n", kk
->test_name
);
1259 printf (" if job_name is omitted, each will be run in turn\n");
1266 global_cond_flag
= TRUE
;
1267 pthread_mutex_lock (&queue_lock
);
1268 pthread_mutex_trylock (&queue_lock
);
1269 pthread_mutex_unlock (&queue_lock
);
1270 sem_post (&global_sema_lock
);
1271 sem_wait (&global_sema_lock
);
1273 sem_post (&fetch_sema_lock
);
1274 sem_wait (&fetch_sema_lock
);
1279 * returns a pointer to a static string in the form:
1280 * Thu 01 Jan 00 00:00:00\0
1281 * 01234567890122345678901234
1282 * ttime is a pointer to a UNIX time in seconds since epoch
1283 * library routine localtime() is used
1286 prtime (time_t *ttime
)
1288 static char *days
[] = {
1289 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1291 static char *months
[] = {
1292 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1293 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1296 static char cvbuf
[26];
1298 /* get the date and time */
1299 struct tm
*tp
= localtime (ttime
);
1301 /* convert to string */
1302 sprintf (cvbuf
, "%3s %02d %s %02d %02d:%02d:%02d",
1303 days
[tp
->tm_wday
], tp
->tm_mday
, months
[tp
->tm_mon
],
1304 tp
->tm_year
% 100, tp
->tm_hour
, tp
->tm_min
, tp
->tm_sec
);