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 */ 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 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