1 /* meas.c -- measure qt stuff. */
5 /* Need this to get assertions under Mach on the Sequent/i386: */
10 fprintf (stderr, "[%s:%d] Assertion " #ex " failed\n", __FILE__, __LINE__); \
18 /* This really ought to be defined in some ANSI include file (*I*
19 think...), but it's defined here instead, which leads us to another
22 The `iaddr_t' type is an integer representation of a pointer,
23 suited for doing arithmetic on addresses, e.g. to round an address
24 to an alignment boundary. */
25 typedef unsigned long iaddr_t;
27 #include <stdarg.h> /* For varargs tryout. */
33 extern void exit (int status);
34 extern int atoi (char const *s);
35 extern int fprintf (FILE *out, char const *fmt, ...);
36 extern int fputs (char const *s, FILE *fp);
37 extern void free (void *sto);
38 extern void *malloc (unsigned nbytes);
39 extern void perror (char const *s);
44 /* Round `v' to be `a'-aligned, assuming `a' is a power of two. */
45 #define ROUND(v, a) (((v) + (a) - 1) & ~((a)-1))
47 typedef struct thread_t {
48 qt_t *qt; /* Pointer to thread of function... */
50 void *top; /* Set top of stack if reuse. */
51 struct thread_t *next;
61 t = malloc (sizeof(thread_t));
66 assert (ssz > QT_STKBASE);
67 t->stk = malloc (ssz);
68 t->stk = (void *)ROUND (((iaddr_t)t->stk), QT_STKALIGN);
73 assert ((((iaddr_t)t->stk) & (QT_STKALIGN-1)) == 0);
74 t->top = QT_SP (t->stk, ssz - QT_STKBASE);
81 t_create (qt_only_t *starter, void *p0, qt_userf_t *f)
86 t->qt = QT_ARGS (t->top, p0, t, f, starter);
100 t_null (qt_t *old, void *p1, void *p2)
102 /* return (garbage); */
107 t_splat (qt_t *old, void *oldp, void *null)
109 *(qt_t **)oldp = old;
110 /* return (garbage); */
114 static char const test01_msg[] =
115 "*QT_SP(sto,sz), QT_ARGS(top,p0,p1,userf,first)";
117 static char const *test01_descr[] = {
118 "Performs 1 QT_SP and one QT_ARGS per iteration.",
122 /* This test gives a guess on how long it takes to initalize
128 char stack[QT_STKBASE+QT_STKALIGN];
132 stk = (char *)ROUND (((iaddr_t)stack), QT_STKALIGN);
137 for (i=0; i<QT_STKBASE; ++i) {
144 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
146 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
147 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
148 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
149 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
151 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
152 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
153 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
154 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
155 top = QT_SP (stk, QT_STKBASE); QT_ARGS (top, 0, 0, 0, 0);
165 static char const test02_msg[] = "QT_BLOCKI (0, 0, test02_aux, t->qt)";
166 static qt_t *rootthread;
169 test02_aux1 (void *pu, void *pt, qt_userf_t *f)
171 QT_ABORT (t_null, 0, 0, rootthread);
175 test02_aux2 (qt_t *old, void *farg1, void *farg2)
178 /* return (garbage); */
187 t = t_create (test02_aux1, 0, 0);
188 QT_BLOCKI (test02_aux2, 0, 0, t->qt);
190 t = t_create (test02_aux1, 0, 0);
191 QT_BLOCKI (test02_aux2, 0, 0, t->qt);
193 t = t_create (test02_aux1, 0, 0);
194 QT_BLOCKI (test02_aux2, 0, 0, t->qt);
196 t = t_create (test02_aux1, 0, 0);
197 QT_BLOCKI (test02_aux2, 0, 0, t->qt);
199 t = t_create (test02_aux1, 0, 0);
200 QT_BLOCKI (test02_aux2, 0, 0, t->qt);
208 static char const test03_msg[] = "QT_BLOCKI (...) test vals are right.";
211 /* Called by the thread function when it wants to shut down.
212 Return a value to the main thread. */
215 test03_aux0 (qt_t *old_is_garbage, void *farg1, void *farg2)
217 assert (farg1 == (void *)5);
218 assert (farg2 == (void *)6);
219 return ((void *)15); /* Some unlikely value. */
223 /* Called during new thread startup by main thread. Since the new
224 thread has never run before, return value is ignored. */
227 test03_aux1 (qt_t *old, void *farg1, void *farg2)
229 assert (old != NULL);
230 assert (farg1 == (void *)5);
231 assert (farg2 == (void *)6);
233 return ((void *)16); /* Different than `15'. */
237 test03_aux2 (void *pu, void *pt, qt_userf_t *f)
239 assert (pu == (void *)1);
240 assert (f == (qt_userf_t *)4);
241 QT_ABORT (test03_aux0, (void *)5, (void *)6, rootthread);
251 t = t_create (test03_aux2, (void *)1, (qt_userf_t *)4);
252 rv = QT_BLOCKI (test03_aux1, (void *)5, (void *)6, t->qt);
253 assert (rv == (void *)15);
261 static char const test04_msg[] = "stp_start w/ no threads.";
267 stp_init(); stp_start();
268 stp_init(); stp_start();
269 stp_init(); stp_start();
270 stp_init(); stp_start();
271 stp_init(); stp_start();
273 stp_init(); stp_start();
274 stp_init(); stp_start();
275 stp_init(); stp_start();
276 stp_init(); stp_start();
277 stp_init(); stp_start();
284 static char const test05_msg[] = "stp w/ 2 yielding thread.";
287 test05_aux (void *null)
298 stp_create (test05_aux, 0);
299 stp_create (test05_aux, 0);
307 static char const test06_msg[] = "*QT_ARGS(...), QT_BLOCKI one thread";
309 static char const *test06_descr[] = {
310 "Does a QT_ARGS, QT_BLOCKI to a helper function that saves the",
311 "stack pointer of the main thread, calls an `only' function that",
312 "saves aborts the thread, calling a null helper function.",
313 ":: start/stop = QT_ARGS + QT_BLOCKI + QT_ABORT + 3 procedure calls.",
317 /* This test initializes a thread, runs it, then returns to the main
318 program, which reinitializes the thread, runs it again, etc. Each
319 iteration corresponds to 1 init, 1 abort, 1 block. */
321 static qt_t *test06_sp;
325 test06_aux2 (void *null0a, void *null1b, void *null2b, qt_userf_t *null)
327 QT_ABORT (t_null, 0, 0, test06_sp);
332 test06_aux3 (qt_t *sp, void *null0c, void *null1c)
335 /* return (garbage); */
344 t = t_create (0, 0, 0);
348 QT_ARGS (t->top, 0, 0, 0, test06_aux2);
349 QT_BLOCKI (test06_aux3, 0, 0, t->qt);
352 QT_ARGS (t->top, 0, 0, 0, test06_aux2);
353 QT_BLOCKI (test06_aux3, 0, 0, t->qt);
356 QT_ARGS (t->top, 0, 0, 0, test06_aux2);
357 QT_BLOCKI (test06_aux3, 0, 0, t->qt);
360 QT_ARGS (t->top, 0, 0, 0, test06_aux2);
361 QT_BLOCKI (test06_aux3, 0, 0, t->qt);
364 QT_ARGS (t->top, 0, 0, 0, test06_aux2);
365 QT_BLOCKI (test06_aux3, 0, 0, t->qt);
374 static char test07_msg[] = "*cswap between threads";
376 static char const *test07_descr[] = {
377 "Build a chain of threads where each thread has a fixed successor.",
378 "There is no scheduling performed. Each thread but one is a loop",
379 "that simply blocks with QT_BLOCKI, calling a helper that saves the",
380 "current stack pointer. The last thread decrements a count, and,",
381 "if zero, aborts back to the main thread. Else it continues with",
382 "the blocking chain. The count is divided by the number of threads",
383 "in the chain, so `n' is the number of integer block operations.",
384 ":: integer cswap = QT_BLOCKI + a procedure call.",
388 /* This test repeatedly blocks a bunch of threads.
389 Each iteration corresponds to one block operation.
391 The threads are arranged so that there are TEST07_N-1 of them that
392 run `test07_aux2'. Each one of those blocks saving it's sp to
393 storage owned by the preceding thread; a pointer to that storage is
394 passed in via `mep'. Each thread has a handle on it's own storage
395 for the next thread, referenced by `nxtp', and it blocks by passing
396 control to `*nxtp', telling the helper function to save its state
397 in `*mep'. The last thread in the chain decrements a count and, if
398 it's gone below zero, returns to `test07'; otherwise, it invokes
399 the first thread in the chain. */
401 static qt_t *test07_heavy;
407 test07_aux2 (void *null0, void *mep, void *nxtp, qt_userf_t *null)
412 nxt = *(qt_t **)nxtp;
414 printf ("Helper 0x%p\n", nxtp);
416 QT_BLOCKI (t_splat, mep, 0, nxt);
421 test07_aux3 (void *np, void *mep, void *nxtp, qt_userf_t *null)
429 QT_ABORT (t_splat, mep, 0, test07_heavy);
431 QT_BLOCKI (t_splat, mep, 0, *(qt_t **)nxtp);
440 thread_t *t[TEST07_N];
442 for (i=0; i<TEST07_N; ++i) {
443 t[i] = t_create (0, 0, 0);
445 for (i=0; i<TEST07_N-1; ++i) {
447 QT_ARGS (t[i]->top, 0, &t[i]->qt, &t[i+1]->qt, test07_aux2);
450 QT_ARGS (t[i]->top, &n, &t[TEST07_N-1]->qt, &t[0]->qt, test07_aux3);
451 QT_BLOCKI (t_splat, &test07_heavy, 0, t[0]->qt);
455 static char test08_msg[] = "Floating-point cswap between threads";
457 static char const *test08_descr[] = {
458 "Measure context switch times including floating-point, use QT_BLOCK.",
462 static qt_t *test08_heavy;
468 test08_aux2 (void *null0, void *mep, void *nxtp, qt_userf_t *null)
473 nxt = *(qt_t **)nxtp;
474 QT_BLOCK (t_splat, mep, 0, nxt);
479 test08_aux3 (void *np, void *mep, void *nxtp, qt_userf_t *null)
487 QT_ABORT (t_splat, mep, 0, test08_heavy);
489 QT_BLOCK (t_splat, mep, 0, *(qt_t **)nxtp);
498 thread_t *t[TEST08_N];
500 for (i=0; i<TEST08_N; ++i) {
501 t[i] = t_create (0, 0, 0);
503 for (i=0; i<TEST08_N-1; ++i) {
505 QT_ARGS (t[i]->top, 0, &t[i]->qt, &t[i+1]->qt, test08_aux2);
508 QT_ARGS (t[i]->top, &n, &t[TEST08_N-1]->qt, &t[0]->qt, test08_aux3);
509 QT_BLOCK (t_splat, &test08_heavy, 0, t[0]->qt);
513 /* Test the varargs procedure calling. */
515 char const test09_msg[] = { "Start and run threads using varargs." };
517 thread_t *test09_t0, *test09_t1, *test09_t2, *test09_main;
520 test09_create (qt_startup_t *start, qt_vuserf_t *f,
521 qt_cleanup_t *cleanup, int nbytes, ...)
527 va_start (ap, nbytes);
528 t->qt = QT_VARGS (t->top, nbytes, ap, t, start, f, cleanup);
535 test09_cleanup (void *pt, void *vuserf_retval)
537 assert (vuserf_retval == (void *)17);
538 QT_ABORT (t_splat, &((thread_t *)pt)->qt, 0,
539 ((thread_t *)pt)->next->qt);
544 test09_start (void *pt)
552 QT_BLOCKI (t_splat, &test09_t0->qt, 0, test09_t1->qt);
557 test09_user2 (int one, int two)
561 QT_BLOCKI (t_splat, &test09_t1->qt, 0, test09_t2->qt);
568 test09_user10 (int one, int two, int three, int four, int five,
569 int six, int seven, int eight, int nine, int ten)
581 QT_BLOCKI (t_splat, &test09_t2->qt, 0, test09_main->qt);
604 test09_t0 = test09_create (test09_start, (qt_vuserf_t*)test09_user0,
606 test09_t1 = test09_create (test09_start, (qt_vuserf_t*)test09_user2,
607 test09_cleanup, 2 * sizeof(qt_word_t), 1, 2);
608 test09_t2 = test09_create (test09_start, (qt_vuserf_t*)test09_user10,
609 test09_cleanup, 10 * sizeof(qt_word_t),
610 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
612 /* Chaining used by `test09_cleanup' to determine who is next. */
613 test09_t0->next = test09_t1;
614 test09_t1->next = test09_t2;
615 test09_t2->next = test09_main;
617 QT_BLOCKI (t_splat, &test09_main->qt, 0, test09_t0->qt);
618 QT_BLOCKI (t_splat, &test09_main->qt, 0, test09_t0->qt);
627 \f/* Test 10/11/12: time the cost of various number of args. */
629 char const test10_msg[] = { "*Test varargs init & startup w/ 0 args." };
631 char const *test10_descr[] = {
632 "Start and stop threads that use variant argument lists (varargs).",
633 "Each thread is initialized by calling a routine that calls",
634 "QT_VARARGS. Then runs the thread by calling QT_BLOCKI to hald the",
635 "main thread, a helper that saves the main thread's stack pointer,",
636 "a null startup function, a null user function, a cleanup function",
637 "that calls QT_ABORT and restarts the main thread. Copies no user",
639 ":: varargs start/stop = QT_BLOCKI + QT_ABORT + 6 function calls.",
643 /* Helper function to send control back to main.
644 Don't save anything. */
647 /* Helper function for starting the varargs thread. Save the stack
648 pointer of the main thread so we can get back there eventually. */
651 /* Startup function for a varargs thread. */
654 test10_startup (void *pt)
659 /* User function for a varargs thread. */
662 test10_run (int arg0, ...)
664 /* return (garbage); */
668 /* Cleanup function for a varargs thread. Send control
669 back to the main thread. Don't save any state from the thread that
673 test10_cleanup (void *pt, void *vuserf_retval)
675 QT_ABORT (t_null, 0, 0, ((thread_t *)pt)->qt);
680 test10_init (thread_t *new, thread_t *next, int nbytes, ...)
684 va_start (ap, nbytes);
685 new->qt = QT_VARGS (new->top, nbytes, ap, next, test10_startup,
686 test10_run, test10_cleanup);
701 test10_init (t, &main, 0);
702 QT_BLOCKI (t_splat, &main.qt, 0, t->qt);
708 char const test11_msg[] = { "*Test varargs init & startup w/ 2 args." };
710 char const *test11_descr[] = {
711 "Varargs initialization/run. Copies 2 user arguments.",
712 ":: varargs 2 start/stop = QT_VARGS(2 args), QT_BLOCKI, QT_ABORT, 6 f() calls.",
727 test10_init (t, &main, 2 * sizeof(int), 2, 1);
728 QT_BLOCKI (t_splat, &main.qt, 0, t->qt);
733 char const test12_msg[] = { "*Test varargs init & startup w/ 4 args." };
735 char const *test12_descr[] = {
736 "Varargs initialization/run. Copies 4 user arguments.",
737 ":: varargs 4 start/stop = QT_VARGS(4 args), QT_BLOCKI, QT_ABORT, 6 f() calls.",
752 test10_init (t, &main, 4 * sizeof(int), 4, 3, 2, 1);
753 QT_BLOCKI (t_splat, &main.qt, 0, t->qt);
759 char const test13_msg[] = { "*Test varargs init & startup w/ 8 args." };
761 char const *test13_descr[] = {
762 "Varargs initialization/run. Copies 8 user arguments.",
763 ":: varargs 8 start/stop = QT_VARGS(8 args), QT_BLOCKI, QT_ABORT, 6 f() calls.",
777 test10_init (t, &main, 8 * sizeof(int), 8, 7, 6, 5, 4, 3, 2, 1);
778 QT_BLOCKI (t_splat, &main.qt, 0, t->qt);
784 char const test14_msg[] = { "*Test varargs initialization w/ 0 args." };
786 char const *test14_descr[] = {
787 "Varargs initialization without running the thread. Just calls",
789 ":: varargs 0 init = QT_VARGS()",
803 test10_init (t, &main, 0 * sizeof(int));
809 char const test15_msg[] = { "*Test varargs initialization w/ 2 args." };
811 char const *test15_descr[] = {
812 "Varargs initialization without running the thread. Just calls",
814 ":: varargs 2 init = QT_VARGS(2 args)",
828 test10_init (t, &main, 2 * sizeof(int), 2, 1);
833 char const test16_msg[] = { "*Test varargs initialization w/ 4 args." };
835 char const *test16_descr[] = {
836 "Varargs initialization without running the thread. Just calls",
838 ":: varargs 4 init = QT_VARGS(4 args)",
853 test10_init (t, &main, 4 * sizeof(int), 4, 3, 2, 1);
859 char const test17_msg[] = { "*Test varargs initialization w/ 8 args." };
861 char const *test17_descr[] = {
862 "Varargs initialization without running the thread. Just calls",
864 ":: varargs 8 init = QT_VARGS(8 args)",
879 test10_init (t, &main, 8 * sizeof(int), 8, 7, 6, 5, 4, 3, 2, 1);
884 \f/* Test times for basic machine operations. */
886 char const test18_msg[] = { "*Call register indirect." };
887 char const *test18_descr[] = { NULL };
896 char const test19_msg[] = { "*Call immediate." };
897 char const *test19_descr[] = { NULL };
906 char const test20_msg[] = { "*Add register-to-register." };
907 char const *test20_descr[] = { NULL };
916 char const test21_msg[] = { "*Load memory to a register." };
917 char const *test21_descr[] = { NULL };
927 typedef struct foo_t {
928 char const *msg; /* Message to print for generic help. */
929 char const **descr; /* A description of what is done by the test. */
934 static foo_t foo[] = {
935 { "Usage:\n", NULL, (void(*)(int n))usage },
936 { test01_msg, test01_descr, test01 },
937 { test02_msg, NULL, test02 },
938 { test03_msg, NULL, test03 },
939 { test04_msg, NULL, test04 },
940 { test05_msg, NULL, test05 },
941 { test06_msg, test06_descr, test06 },
942 { test07_msg, test07_descr, test07 },
943 { test08_msg, test08_descr, test08 },
944 { test09_msg, NULL, test09 },
945 { test10_msg, test10_descr, test10 },
946 { test11_msg, test11_descr, test11 },
947 { test12_msg, test12_descr, test12 },
948 { test13_msg, test13_descr, test13 },
949 { test14_msg, test14_descr, test14 },
950 { test15_msg, test15_descr, test15 },
951 { test16_msg, test16_descr, test16 },
952 { test17_msg, test17_descr, test17 },
953 { test18_msg, test18_descr, test18 },
954 { test19_msg, test19_descr, test19 },
955 { test20_msg, test20_descr, test20 },
956 { test21_msg, test21_descr, test21 },
966 fprintf (stderr, "tracer\t%d\n", tv++);
973 fprintf (stderr, "tracer2\t%d val=0x%p", tv++, val);
984 for (i=0; foo[i].msg; ++i) {
989 fprintf (out, "[%d]\n", i);
990 for (j=0; foo[i].descr[j]; ++j) {
991 fputs (foo[i].descr[j], out);
1005 fputs (foo[0].msg, stderr);
1006 for (i=1; foo[i].msg; ++i) {
1007 fprintf (stderr, "%2d\t%s\n", i, foo[i].msg);
1014 args (int *which, int *n, int argc, char **argv)
1016 static int nfuncs = 0;
1018 if (argc == 2 && argv[1][0] == '-' && argv[1][1] == 'h') {
1023 for (nfuncs=0; foo[nfuncs].msg; ++nfuncs)
1027 if (argc != 2 && argc != 3) {
1031 *which = atoi (argv[1]);
1032 if (*which < 0 || *which >= nfuncs) {
1042 main (int argc, char **argv)
1045 args (&which, &n, argc, argv);
1046 (*(foo[which].f))(n);