1 /*
2 * Copyright (c) 2005, Bull S.A.. All rights reserved.
3 * Created by: Sebastien Decugis
4
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
17 * This scalability sample aims to test the following assertion:
18 * -> The sem_open() duration does not depend on the # of opened semaphores
19 * in the system
20
21 * The steps are:
22 * -> Create semaphores until failure
23
24 * The test fails if the sem_open duration tends to grow with the # of semaphores,
25 * or if the failure at last semaphore creation is unexpected.
26 */
27
28 /* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
29 #define _POSIX_C_SOURCE 200112L
30
31 /********************************************************************************************/
32 /****************************** standard includes *****************************************/
33 /********************************************************************************************/
34 #include <pthread.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <unistd.h>
40
41 #include <math.h>
42 #include <errno.h>
43 #include <time.h>
44 #include <semaphore.h>
45 #include <fcntl.h>
46
47 /********************************************************************************************/
48 /****************************** Test framework *****************************************/
49 /********************************************************************************************/
50 #include "testfrmw.h"
51 #include "testfrmw.c"
52 /* This header is responsible for defining the following macros:
53 * UNRESOLVED(ret, descr);
54 * where descr is a description of the error and ret is an int (error code for example)
55 * FAILED(descr);
56 * where descr is a short text saying why the test has failed.
57 * PASSED();
58 * No parameter.
59 *
60 * Both three macros shall terminate the calling process.
61 * The testcase shall not terminate in any other maneer.
62 *
63 * The other file defines the functions
64 * void output_init()
65 * void output(char * string, ...)
66 *
67 * Those may be used to output information.
68 */
69
70 /********************************************************************************************/
71 /********************************** Configuration ******************************************/
72 /********************************************************************************************/
73 #ifndef SCALABILITY_FACTOR
74 #define SCALABILITY_FACTOR 1
75 #endif
76 #ifndef VERBOSE
77 #define VERBOSE 1
78 #endif
79
80 #define BLOCKSIZE (100 * SCALABILITY_FACTOR)
81
82 #ifdef PLOT_OUTPUT
83 #undef VERBOSE
84 #define VERBOSE 0
85 #endif
86
87 /********************************************************************************************/
88 /*********************************** Test *****************************************/
89 /********************************************************************************************/
90
91 /* The next structure is used to save the tests measures */
92
93 typedef struct __mes_t {
94 int nsem;
95 long _data_open; /* As we store µsec values, a long type should be enough. */
96 long _data_close; /* As we store µsec values, a long type should be enough. */
97
98 struct __mes_t *next;
99
100 struct __mes_t *prev;
101 } mes_t;
102
103 /* Forward declaration */
104 int parse_measure(mes_t * measures);
105
106 /* Structure to store created semaphores */
107
108 typedef struct __test_t {
109 sem_t *sems[BLOCKSIZE];
110
111 struct __test_t *next;
112
113 struct __test_t *prev;
114 } test_t;
115
116 /* Test routine */
main(int argc,char * argv[])117 int main(int argc, char *argv[])
118 {
119 int ret, status, locerrno;
120 int nsem, i;
121
122 struct timespec ts_ref, ts_fin;
123 mes_t sentinel;
124 mes_t *m_cur, *m_tmp;
125
126 char sem_name[255];
127 test_t sems;
128
129 struct __test_t *sems_cur = &sems, *sems_tmp;
130
131 long SEM_MAX = sysconf(_SC_SEM_NSEMS_MAX);
132
133 /* Initialize the measure list */
134 m_cur = &sentinel;
135 m_cur->next = NULL;
136 m_cur->prev = NULL;
137
138 /* Initialize output routine */
139 output_init();
140
141 /* Initialize sems */
142 sems_cur->next = NULL;
143 sems_cur->prev = NULL;
144
145 #if VERBOSE > 1
146 output("SEM_NSEMS_MAX: %ld\n", SEM_MAX);
147
148 #endif
149
150 #ifdef PLOT_OUTPUT
151 output("# COLUMNS 3 Semaphores sem_open sem_close\n");
152
153 #endif
154
155 nsem = 0;
156 status = 0;
157
158 while (1) { /* we will break */
159 /* Create a new block */
160 sems_tmp = malloc(sizeof(test_t));
161
162 if (sems_tmp == NULL) {
163 /* We stop here */
164 #if VERBOSE > 0
165 output("malloc failed with error %d (%s)\n", errno,
166 strerror(errno));
167 #endif
168 /* We can proceed anyway */
169 status = 1;
170
171 break;
172 }
173
174 /* read clock */
175 ret = clock_gettime(CLOCK_REALTIME, &ts_ref);
176
177 if (ret != 0) {
178 UNRESOLVED(errno, "Unable to read clock");
179 }
180
181 /* Open all semaphores in the current block */
182 for (i = 0; i < BLOCKSIZE; i++) {
183 sprintf(sem_name, "/sem_open_scal_s%d", nsem);
184 sems_tmp->sems[i] =
185 sem_open(sem_name, O_CREAT, 0777, 1);
186
187 if (sems_tmp->sems[i] == SEM_FAILED) {
188 #if VERBOSE > 0
189 output("sem_open failed with error %d (%s)\n",
190 errno, strerror(errno));
191 #endif
192 /* Check error code */
193
194 switch (errno) {
195 case EMFILE:
196 case ENFILE:
197 case ENOSPC:
198 case ENOMEM:
199 status = 2;
200 break;
201 default:
202 UNRESOLVED(errno, "Unexpected error!");
203 }
204
205 break;
206 }
207
208 if ((SEM_MAX > 0) && (nsem > SEM_MAX)) {
209 /* Erreur */
210 FAILED
211 ("sem_open opened more than SEM_NSEMS_MAX semaphores");
212 }
213
214 nsem++;
215 }
216
217 /* read clock */
218 ret = clock_gettime(CLOCK_REALTIME, &ts_fin);
219
220 if (ret != 0) {
221 UNRESOLVED(errno, "Unable to read clock");
222 }
223
224 if (status == 2) {
225 /* We were not able to fill this bloc, so we can discard it */
226
227 for (--i; i >= 0; i--) {
228 ret = sem_close(sems_tmp->sems[i]);
229
230 if (ret != 0) {
231 UNRESOLVED(errno, "Failed to close");
232 }
233
234 }
235
236 free(sems_tmp);
237 break;
238
239 }
240
241 sems_tmp->prev = sems_cur;
242 sems_cur->next = sems_tmp;
243 sems_cur = sems_tmp;
244 sems_cur->next = NULL;
245
246 /* add to the measure list */
247 m_tmp = malloc(sizeof(mes_t));
248
249 if (m_tmp == NULL) {
250 /* We stop here */
251 #if VERBOSE > 0
252 output("malloc failed with error %d (%s)\n", errno,
253 strerror(errno));
254 #endif
255 /* We can proceed anyway */
256 status = 3;
257
258 break;
259 }
260
261 m_tmp->nsem = nsem;
262 m_tmp->next = NULL;
263 m_tmp->prev = m_cur;
264 m_cur->next = m_tmp;
265
266 m_cur = m_tmp;
267
268 m_cur->_data_open =
269 ((ts_fin.tv_sec - ts_ref.tv_sec) * 1000000) +
270 ((ts_fin.tv_nsec - ts_ref.tv_nsec) / 1000);
271 m_cur->_data_close = 0;
272 }
273
274 locerrno = errno;
275
276 /* Unlink all existing semaphores */
277 #if VERBOSE > 0
278 output("Unlinking %d semaphores\n", nsem);
279 #endif
280
281 for (i = 0; i <= nsem; i++) {
282 sprintf(sem_name, "/sem_open_scal_s%d", i);
283 sem_unlink(sem_name);
284 }
285
286 /* Free all semaphore blocs */
287 #if VERBOSE > 0
288 output("Close and free semaphores (this can take up to 10 minutes)\n");
289
290 #endif
291
292 /* Reverse list order */
293 while (sems_cur != &sems) {
294 /* read clock */
295 ret = clock_gettime(CLOCK_REALTIME, &ts_ref);
296
297 if (ret != 0) {
298 UNRESOLVED(errno, "Unable to read clock");
299 }
300
301 /* Empty the sems_cur block */
302
303 for (i = 0; i < BLOCKSIZE; i++) {
304 ret = sem_close(sems_cur->sems[i]);
305
306 if (ret != 0) {
307 UNRESOLVED(errno,
308 "Failed to close a semaphore");
309 }
310 }
311
312 /* read clock */
313 ret = clock_gettime(CLOCK_REALTIME, &ts_fin);
314
315 if (ret != 0) {
316 UNRESOLVED(errno, "Unable to read clock");
317 }
318
319 /* add this measure to measure list */
320
321 m_cur->_data_close =
322 ((ts_fin.tv_sec - ts_ref.tv_sec) * 1000000) +
323 ((ts_fin.tv_nsec - ts_ref.tv_nsec) / 1000);
324
325 m_cur = m_cur->prev;
326
327 /* remove the sem bloc */
328 sems_cur = sems_cur->prev;
329
330 free(sems_cur->next);
331
332 sems_cur->next = NULL;
333 }
334
335 #if VERBOSE > 0
336 output("Parse results\n");
337
338 #endif
339
340 /* Compute the results */
341 ret = parse_measure(&sentinel);
342
343 /* Free the resources and output the results */
344
345 #if VERBOSE > 5
346 output("Dump : \n");
347
348 output(" nsem | open | close \n");
349
350 #endif
351
352 while (sentinel.next != NULL) {
353 m_cur = sentinel.next;
354 #if (VERBOSE > 5) || defined(PLOT_OUTPUT)
355 output("%8.8i %1.1li.%6.6li %1.1li.%6.6li\n", m_cur->nsem,
356 m_cur->_data_open / 1000000, m_cur->_data_open % 1000000,
357 m_cur->_data_close / 1000000,
358 m_cur->_data_close % 1000000);
359
360 #endif
361 sentinel.next = m_cur->next;
362
363 free(m_cur);
364 }
365
366 if (ret != 0) {
367 FAILED
368 ("The function is not scalable, add verbosity for more information");
369 }
370
371 /* Check status */
372 if (status) {
373 UNRESOLVED(locerrno,
374 "Function is scalable, but test terminated with error");
375 }
376 #if VERBOSE > 0
377 output("-----\n");
378
379 output("All test data destroyed\n");
380
381 output("Test PASSED\n");
382
383 #endif
384
385 PASSED;
386 }
387
388 /***
389 * The next function will seek for the better model for each series of measurements.
390 *
391 * The tested models are: -- X = # threads; Y = latency
392 * -> Y = a; -- Error is r1 = avg((Y - Yavg)²);
393 * -> Y = aX + b; -- Error is r2 = avg((Y -aX -b)²);
394 * -- where a = avg ((X - Xavg)(Y - Yavg)) / avg((X - Xavg)²)
395 * -- Note: We will call _q = sum((X - Xavg) * (Y - Yavg));
396 * -- and _d = sum((X - Xavg)²);
397 * -- and b = Yavg - a * Xavg
398 * -> Y = c * X^a;-- Same as previous, but with log(Y) = a log(X) + b; and b = log(c). Error is r3
399 * -> Y = exp(aX + b); -- log(Y) = aX + b. Error is r4
400 *
401 * We compute each error factor (r1, r2, r3, r4) then search which is the smallest (with ponderation).
402 * The function returns 0 when r1 is the best for all cases (latency is constant) and !0 otherwise.
403 */
404
405 struct row {
406 long X; /* the X values -- copied from function argument */
407 long Y_o; /* the Y values -- copied from function argument */
408 long Y_c; /* the Y values -- copied from function argument */
409 long _x; /* Value X - Xavg */
410 long _y_o; /* Value Y - Yavg */
411 long _y_c; /* Value Y - Yavg */
412 double LnX; /* Natural logarithm of X values */
413 double LnY_o; /* Natural logarithm of Y values */
414 double LnY_c; /* Natural logarithm of Y values */
415 double _lnx; /* Value LnX - LnXavg */
416 double _lny_o; /* Value LnY - LnYavg */
417 double _lny_c; /* Value LnY - LnYavg */
418 };
419
parse_measure(mes_t * measures)420 int parse_measure(mes_t * measures)
421 {
422 int ret, r;
423
424 mes_t *cur;
425
426 double Xavg, Yavg_o, Yavg_c;
427 double LnXavg, LnYavg_o, LnYavg_c;
428
429 int N;
430
431 double r1_o, r2_o, r3_o, r4_o;
432 double r1_c, r2_c, r3_c, r4_c;
433
434 /* Some more intermediate vars */
435 long double _q_o[3];
436 long double _d_o[3];
437 long double _q_c[3];
438 long double _d_c[3];
439
440 long double t; /* temp value */
441
442 struct row *Table = NULL;
443
444 /* This array contains the last element of each serie */
445 int array_max;
446
447 /* Initialize the datas */
448
449 array_max = -1; /* means no data */
450 Xavg = 0.0;
451 LnXavg = 0.0;
452 Yavg_o = 0.0;
453 LnYavg_o = 0.0;
454 r1_o = 0.0;
455 r2_o = 0.0;
456 r3_o = 0.0;
457 r4_o = 0.0;
458 _q_o[0] = 0.0;
459 _q_o[1] = 0.0;
460 _q_o[2] = 0.0;
461 _d_o[0] = 0.0;
462 _d_o[1] = 0.0;
463 _d_o[2] = 0.0;
464 Yavg_c = 0.0;
465 LnYavg_c = 0.0;
466 r1_c = 0.0;
467 r2_c = 0.0;
468 r3_c = 0.0;
469 r4_c = 0.0;
470 _q_c[0] = 0.0;
471 _q_c[1] = 0.0;
472 _q_c[2] = 0.0;
473 _d_c[0] = 0.0;
474 _d_c[1] = 0.0;
475 _d_c[2] = 0.0;
476
477 N = 0;
478 cur = measures;
479
480 #if VERBOSE > 1
481 output("Data analysis starting\n");
482 #endif
483
484 /* We start with reading the list to find:
485 * -> number of elements, to assign an array.
486 * -> average values
487 */
488
489 while (cur->next != NULL) {
490 cur = cur->next;
491
492 N++;
493
494 if (cur->_data_open != 0) {
495 array_max = N;
496 Xavg += (double)cur->nsem;
497 LnXavg += log((double)cur->nsem);
498 Yavg_o += (double)cur->_data_open;
499 LnYavg_o += log((double)cur->_data_open);
500 Yavg_c += (double)cur->_data_close;
501 LnYavg_c += log((double)cur->_data_close);
502 }
503
504 }
505
506 /* We have the sum; we can divide to obtain the average values */
507 if (array_max != -1) {
508 Xavg /= array_max;
509 LnXavg /= array_max;
510 Yavg_o /= array_max;
511 LnYavg_o /= array_max;
512 Yavg_c /= array_max;
513 LnYavg_c /= array_max;
514 }
515 #if VERBOSE > 1
516 output(" Found %d rows\n", N);
517
518 #endif
519
520 /* We will now alloc the array ... */
521
522 Table = calloc(N, sizeof(struct row));
523
524 if (Table == NULL) {
525 UNRESOLVED(errno, "Unable to alloc space for results parsing");
526 }
527
528 /* ... and fill it */
529 N = 0;
530
531 cur = measures;
532
533 while (cur->next != NULL) {
534 cur = cur->next;
535
536 Table[N].X = (long)cur->nsem;
537 Table[N].LnX = log((double)cur->nsem);
538
539 if (array_max > N) {
540 Table[N]._x = Table[N].X - Xavg;
541 Table[N]._lnx = Table[N].LnX - LnXavg;
542 Table[N].Y_o = cur->_data_open;
543 Table[N]._y_o = Table[N].Y_o - Yavg_o;
544 Table[N].LnY_o = log((double)cur->_data_open);
545 Table[N]._lny_o = Table[N].LnY_o - LnYavg_o;
546 Table[N].Y_c = cur->_data_close;
547 Table[N]._y_c = Table[N].Y_c - Yavg_c;
548 Table[N].LnY_c = log((double)cur->_data_close);
549 Table[N]._lny_c = Table[N].LnY_c - LnYavg_c;
550 }
551
552 N++;
553 }
554
555 /* We won't need the list anymore -- we'll work with the array which should be faster. */
556 #if VERBOSE > 1
557 output(" Data was stored in an array.\n");
558
559 #endif
560
561 /* We need to read the full array at least twice to compute all the error factors */
562
563 /* In the first pass, we'll compute:
564 * -> r1 for each scenar.
565 * -> "a" factor for linear (0), power (1) and exponential (2) approximations -- with using the _d and _q vars.
566 */
567 #if VERBOSE > 1
568 output("Starting first pass...\n");
569
570 #endif
571 for (r = 0; r < array_max; r++) {
572 r1_o +=
573 ((double)Table[r]._y_o / array_max) * (double)Table[r]._y_o;
574
575 _q_o[0] += Table[r]._y_o * Table[r]._x;
576 _d_o[0] += Table[r]._x * Table[r]._x;
577
578 _q_o[1] += Table[r]._lny_o * Table[r]._lnx;
579 _d_o[1] += Table[r]._lnx * Table[r]._lnx;
580
581 _q_o[2] += Table[r]._lny_o * Table[r]._x;
582 _d_o[2] += Table[r]._x * Table[r]._x;
583
584 r1_c +=
585 ((double)Table[r]._y_c / array_max) * (double)Table[r]._y_c;
586
587 _q_c[0] += Table[r]._y_c * Table[r]._x;
588 _d_c[0] += Table[r]._x * Table[r]._x;
589
590 _q_c[1] += Table[r]._lny_c * Table[r]._lnx;
591 _d_c[1] += Table[r]._lnx * Table[r]._lnx;
592
593 _q_c[2] += Table[r]._lny_c * Table[r]._x;
594 _d_c[2] += Table[r]._x * Table[r]._x;
595
596 }
597
598 /* First pass is terminated; a2 = _q[0]/_d[0]; a3 = _q[1]/_d[1]; a4 = _q[2]/_d[2] */
599
600 /* In the first pass, we'll compute:
601 * -> r2, r3, r4 for each scenar.
602 */
603
604 #if VERBOSE > 1
605 output("Starting second pass...\n");
606
607 #endif
608 for (r = 0; r < array_max; r++) {
609 /* r2 = avg((y - ax -b)²); t = (y - ax - b) = (y - yavg) - a (x - xavg); */
610 t = (Table[r]._y_o - ((_q_o[0] * Table[r]._x) / _d_o[0]));
611 r2_o += t * t / array_max;
612
613 t = (Table[r]._y_c - ((_q_c[0] * Table[r]._x) / _d_c[0]));
614 r2_c += t * t / array_max;
615
616 /* r3 = avg((y - c.x^a) ²);
617 t = y - c * x ^ a
618 = y - log (LnYavg - (_q[1]/_d[1]) * LnXavg) * x ^ (_q[1]/_d[1])
619 */
620 t = (Table[r].Y_o
621 - (logl(LnYavg_o - (_q_o[1] / _d_o[1]) * LnXavg)
622 * powl(Table[r].X, (_q_o[1] / _d_o[1]))
623 ));
624 r3_o += t * t / array_max;
625
626 t = (Table[r].Y_c
627 - (logl(LnYavg_c - (_q_c[1] / _d_c[1]) * LnXavg)
628 * powl(Table[r].X, (_q_c[1] / _d_c[1]))
629 ));
630 r3_c += t * t / array_max;
631
632 /* r4 = avg((y - exp(ax+b))²);
633 t = y - exp(ax+b)
634 = y - exp(_q[2]/_d[2] * x + (LnYavg - (_q[2]/_d[2] * Xavg)));
635 = y - exp(_q[2]/_d[2] * (x - Xavg) + LnYavg);
636 */
637 t = (Table[r].Y_o
638 - expl((_q_o[2] / _d_o[2]) * Table[r]._x + LnYavg_o));
639 r4_o += t * t / array_max;
640
641 t = (Table[r].Y_c
642 - expl((_q_c[2] / _d_c[2]) * Table[r]._x + LnYavg_c));
643 r4_c += t * t / array_max;
644
645 }
646
647 #if VERBOSE > 1
648 output("All computing terminated.\n");
649
650 #endif
651 ret = 0;
652
653 #if VERBOSE > 1
654 output(" # of data: %i\n", array_max);
655
656 output(" Model: Y = k\n");
657
658 output(" sem_open:\n");
659
660 output(" k = %g\n", Yavg_o);
661
662 output(" Divergence %g\n", r1_o);
663
664 output(" sem_close:\n");
665
666 output(" k = %g\n", Yavg_c);
667
668 output(" Divergence %g\n", r1_c);
669
670 output(" Model: Y = a * X + b\n");
671
672 output(" sem_open:\n");
673
674 output(" a = %Lg\n", _q_o[0] / _d_o[0]);
675
676 output(" b = %Lg\n", Yavg_o - ((_q_o[0] / _d_o[0]) * Xavg));
677
678 output(" Divergence %g\n", r2_o);
679
680 output(" sem_close:\n");
681
682 output(" a = %Lg\n", _q_c[0] / _d_c[0]);
683
684 output(" b = %Lg\n", Yavg_c - ((_q_c[0] / _d_c[0]) * Xavg));
685
686 output(" Divergence %g\n", r2_c);
687
688 output(" Model: Y = c * X ^ a\n");
689
690 output(" sem_open:\n");
691
692 output(" a = %Lg\n", _q_o[1] / _d_o[1]);
693
694 output(" c = %Lg\n",
695 logl(LnYavg_o - (_q_o[1] / _d_o[1]) * LnXavg));
696
697 output(" Divergence %g\n", r3_o);
698
699 output(" sem_close:\n");
700
701 output(" a = %Lg\n", _q_c[1] / _d_c[1]);
702
703 output(" c = %Lg\n",
704 logl(LnYavg_c - (_q_c[1] / _d_c[1]) * LnXavg));
705
706 output(" Divergence %g\n", r3_c);
707
708 output(" Model: Y = exp(a * X + b)\n");
709
710 output(" sem_open:\n");
711
712 output(" a = %Lg\n", _q_o[2] / _d_o[2]);
713
714 output(" b = %Lg\n", LnYavg_o - ((_q_o[2] / _d_o[2]) * Xavg));
715
716 output(" Divergence %g\n", r4_o);
717
718 output(" sem_close:\n");
719
720 output(" a = %Lg\n", _q_c[2] / _d_c[2]);
721
722 output(" b = %Lg\n", LnYavg_c - ((_q_c[2] / _d_c[2]) * Xavg));
723
724 output(" Divergence %g\n", r4_c);
725
726 #endif
727
728 if (array_max != -1) {
729 /* Compare r1 to other values, with some ponderations */
730
731 if ((r1_o > 1.1 * r2_o) || (r1_o > 1.2 * r3_o) ||
732 (r1_o > 1.3 * r4_o) || (r1_c > 1.1 * r2_c) ||
733 (r1_c > 1.2 * r3_c) || (r1_c > 1.3 * r4_c))
734 ret++;
735
736 #if VERBOSE > 1
737 else
738 output(" Sanction: OK\n");
739
740 #endif
741
742 }
743
744 /* We need to free the array */
745 free(Table);
746
747 /* We're done */
748 return ret;
749 }
750