You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

499 lines
14 KiB

  1. #include <argp.h>
  2. #include <assert.h>
  3. #include <inttypes.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <sys/time.h>
  8. #ifdef HAVE_PROFILER
  9. #include <gperftools/profiler.h>
  10. #endif
  11. #include <getrss.h>
  12. #include <sylvan.h>
  13. #include <llmsset.h>
  14. /* Configuration */
  15. static int report_levels = 0; // report states at start of every level
  16. static int report_table = 0; // report table size at end of every level
  17. static int strategy = 1; // set to 1 = use PAR strategy; set to 0 = use BFS strategy
  18. static int check_deadlocks = 0; // set to 1 to check for deadlocks
  19. static int print_transition_matrix = 1; // print transition relation matrix
  20. static int workers = 0; // autodetect
  21. static char* model_filename = NULL; // filename of model
  22. #ifdef HAVE_PROFILER
  23. static char* profile_filename = NULL; // filename for profiling
  24. #endif
  25. /* argp configuration */
  26. static struct argp_option options[] =
  27. {
  28. {"workers", 'w', "<workers>", 0, "Number of workers (default=0: autodetect)", 0},
  29. {"strategy", 's', "<bfs|par|sat>", 0, "Strategy for reachability (default=par)", 0},
  30. #ifdef HAVE_PROFILER
  31. {"profiler", 'p', "<filename>", 0, "Filename for profiling", 0},
  32. #endif
  33. {"deadlocks", 3, 0, 0, "Check for deadlocks", 1},
  34. {"count-states", 1, 0, 0, "Report #states at each level", 1},
  35. {"count-table", 2, 0, 0, "Report table usage at each level", 1},
  36. {0, 0, 0, 0, 0, 0}
  37. };
  38. static error_t
  39. parse_opt(int key, char *arg, struct argp_state *state)
  40. {
  41. switch (key) {
  42. case 'w':
  43. workers = atoi(arg);
  44. break;
  45. case 's':
  46. if (strcmp(arg, "bfs")==0) strategy = 0;
  47. else if (strcmp(arg, "par")==0) strategy = 1;
  48. else if (strcmp(arg, "sat")==0) strategy = 2;
  49. else argp_usage(state);
  50. break;
  51. case 3:
  52. check_deadlocks = 1;
  53. break;
  54. case 1:
  55. report_levels = 1;
  56. break;
  57. case 2:
  58. report_table = 1;
  59. break;
  60. #ifdef HAVE_PROFILER
  61. case 'p':
  62. profile_filename = arg;
  63. break;
  64. #endif
  65. case ARGP_KEY_ARG:
  66. if (state->arg_num >= 1) argp_usage(state);
  67. model_filename = arg;
  68. break;
  69. case ARGP_KEY_END:
  70. if (state->arg_num < 1) argp_usage(state);
  71. break;
  72. default:
  73. return ARGP_ERR_UNKNOWN;
  74. }
  75. return 0;
  76. }
  77. static struct argp argp = { options, parse_opt, "<model>", 0, 0, 0, 0 };
  78. /* Globals */
  79. typedef struct set
  80. {
  81. MDD mdd;
  82. MDD proj;
  83. int size;
  84. } *set_t;
  85. typedef struct relation
  86. {
  87. MDD mdd;
  88. MDD meta;
  89. int size;
  90. } *rel_t;
  91. static size_t vector_size; // size of vector
  92. static int next_count; // number of partitions of the transition relation
  93. static rel_t *next; // each partition of the transition relation
  94. #define Abort(...) { fprintf(stderr, __VA_ARGS__); exit(-1); }
  95. /* Load a set from file */
  96. static set_t
  97. set_load(FILE* f)
  98. {
  99. lddmc_serialize_fromfile(f);
  100. size_t mdd;
  101. size_t proj;
  102. int size;
  103. if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  104. if (fread(&proj, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  105. if (fread(&size, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  106. LACE_ME;
  107. set_t set = (set_t)malloc(sizeof(struct set));
  108. set->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd));
  109. set->proj = lddmc_ref(lddmc_serialize_get_reversed(proj));
  110. set->size = size;
  111. return set;
  112. }
  113. static int
  114. calculate_size(MDD meta)
  115. {
  116. int result = 0;
  117. uint32_t val = lddmc_getvalue(meta);
  118. while (val != (uint32_t)-1) {
  119. if (val != 0) result += 1;
  120. meta = lddmc_follow(meta, val);
  121. assert(meta != lddmc_true && meta != lddmc_false);
  122. val = lddmc_getvalue(meta);
  123. }
  124. return result;
  125. }
  126. /* Load a relation from file */
  127. static rel_t
  128. rel_load(FILE* f)
  129. {
  130. lddmc_serialize_fromfile(f);
  131. size_t mdd;
  132. size_t meta;
  133. if (fread(&mdd, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  134. if (fread(&meta, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  135. LACE_ME;
  136. rel_t rel = (rel_t)malloc(sizeof(struct relation));
  137. rel->mdd = lddmc_ref(lddmc_serialize_get_reversed(mdd));
  138. rel->meta = lddmc_ref(lddmc_serialize_get_reversed(meta));
  139. rel->size = calculate_size(rel->meta);
  140. return rel;
  141. }
  142. static void
  143. print_example(MDD example)
  144. {
  145. if (example != lddmc_false) {
  146. LACE_ME;
  147. uint32_t vec[vector_size];
  148. lddmc_sat_one(example, vec, vector_size);
  149. size_t i;
  150. printf("[");
  151. for (i=0; i<vector_size; i++) {
  152. if (i>0) printf(",");
  153. printf("%" PRIu32, vec[i]);
  154. }
  155. printf("]");
  156. }
  157. }
  158. static void
  159. print_matrix(size_t size, MDD meta)
  160. {
  161. if (size == 0) return;
  162. uint32_t val = lddmc_getvalue(meta);
  163. if (val == 1) {
  164. printf("+");
  165. print_matrix(size-1, lddmc_follow(lddmc_follow(meta, 1), 2));
  166. } else {
  167. if (val == (uint32_t)-1) printf("-");
  168. else if (val == 0) printf("-");
  169. else if (val == 3) printf("r");
  170. else if (val == 4) printf("w");
  171. print_matrix(size-1, lddmc_follow(meta, val));
  172. }
  173. }
  174. static char*
  175. to_h(double size, char *buf)
  176. {
  177. const char* units[] = {"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"};
  178. int i = 0;
  179. for (;size>1024;size/=1024) i++;
  180. sprintf(buf, "%.*f %s", i, size, units[i]);
  181. return buf;
  182. }
  183. static int
  184. get_first(MDD meta)
  185. {
  186. uint32_t val = lddmc_getvalue(meta);
  187. if (val != 0) return 0;
  188. return 1+get_first(lddmc_follow(meta, val));
  189. }
  190. /* Straight-forward implementation of parallel reduction */
  191. TASK_5(MDD, go_par, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks)
  192. {
  193. if (len == 1) {
  194. // Calculate NEW successors (not in visited)
  195. MDD succ = lddmc_ref(lddmc_relprod(cur, next[from]->mdd, next[from]->meta));
  196. if (deadlocks) {
  197. // check which MDDs in deadlocks do not have a successor in this relation
  198. MDD anc = lddmc_ref(lddmc_relprev(succ, next[from]->mdd, next[from]->meta, cur));
  199. *deadlocks = lddmc_ref(lddmc_minus(*deadlocks, anc));
  200. lddmc_deref(anc);
  201. }
  202. MDD result = lddmc_ref(lddmc_minus(succ, visited));
  203. lddmc_deref(succ);
  204. return result;
  205. } else {
  206. MDD deadlocks_left;
  207. MDD deadlocks_right;
  208. if (deadlocks) {
  209. deadlocks_left = *deadlocks;
  210. deadlocks_right = *deadlocks;
  211. }
  212. // Recursively calculate left+right
  213. SPAWN(go_par, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left: NULL);
  214. MDD right = CALL(go_par, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL);
  215. MDD left = SYNC(go_par);
  216. // Merge results of left+right
  217. MDD result = lddmc_ref(lddmc_union(left, right));
  218. lddmc_deref(left);
  219. lddmc_deref(right);
  220. if (deadlocks) {
  221. *deadlocks = lddmc_ref(lddmc_intersect(deadlocks_left, deadlocks_right));
  222. lddmc_deref(deadlocks_left);
  223. lddmc_deref(deadlocks_right);
  224. }
  225. return result;
  226. }
  227. }
  228. /* PAR strategy, parallel strategy (operations called in parallel *and* parallelized by Sylvan) */
  229. VOID_TASK_1(par, set_t, set)
  230. {
  231. MDD visited = set->mdd;
  232. MDD new = lddmc_ref(visited);
  233. size_t counter = 1;
  234. do {
  235. char buf[32];
  236. to_h(getCurrentRSS(), buf);
  237. printf("Memory usage: %s\n", buf);
  238. printf("Level %zu... ", counter++);
  239. if (report_levels) {
  240. printf("%zu states... ", (size_t)lddmc_satcount_cached(visited));
  241. }
  242. fflush(stdout);
  243. // calculate successors in parallel
  244. MDD cur = new;
  245. MDD deadlocks = cur;
  246. new = CALL(go_par, cur, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL);
  247. lddmc_deref(cur);
  248. if (check_deadlocks) {
  249. printf("found %zu deadlock states... ", (size_t)lddmc_satcount_cached(deadlocks));
  250. if (deadlocks != lddmc_false) {
  251. printf("example: ");
  252. print_example(deadlocks);
  253. printf("... ");
  254. check_deadlocks = 0;
  255. }
  256. }
  257. // visited = visited + new
  258. MDD old_visited = visited;
  259. visited = lddmc_ref(lddmc_union(visited, new));
  260. lddmc_deref(old_visited);
  261. if (report_table) {
  262. size_t filled, total;
  263. sylvan_table_usage(&filled, &total);
  264. printf("done, table: %0.1f%% full (%zu nodes).\n", 100.0*(double)filled/total, filled);
  265. } else {
  266. printf("done.\n");
  267. }
  268. } while (new != lddmc_false);
  269. lddmc_deref(new);
  270. set->mdd = visited;
  271. }
  272. /* Sequential version of merge-reduction */
  273. TASK_5(MDD, go_bfs, MDD, cur, MDD, visited, size_t, from, size_t, len, MDD*, deadlocks)
  274. {
  275. if (len == 1) {
  276. // Calculate NEW successors (not in visited)
  277. MDD succ = lddmc_ref(lddmc_relprod(cur, next[from]->mdd, next[from]->meta));
  278. if (deadlocks) {
  279. // check which MDDs in deadlocks do not have a successor in this relation
  280. MDD anc = lddmc_ref(lddmc_relprev(succ, next[from]->mdd, next[from]->meta, cur));
  281. *deadlocks = lddmc_ref(lddmc_minus(*deadlocks, anc));
  282. lddmc_deref(anc);
  283. }
  284. MDD result = lddmc_ref(lddmc_minus(succ, visited));
  285. lddmc_deref(succ);
  286. return result;
  287. } else {
  288. MDD deadlocks_left;
  289. MDD deadlocks_right;
  290. if (deadlocks) {
  291. deadlocks_left = *deadlocks;
  292. deadlocks_right = *deadlocks;
  293. }
  294. // Recursively calculate left+right
  295. MDD left = CALL(go_bfs, cur, visited, from, (len+1)/2, deadlocks ? &deadlocks_left : NULL);
  296. MDD right = CALL(go_bfs, cur, visited, from+(len+1)/2, len/2, deadlocks ? &deadlocks_right : NULL);
  297. // Merge results of left+right
  298. MDD result = lddmc_ref(lddmc_union(left, right));
  299. lddmc_deref(left);
  300. lddmc_deref(right);
  301. if (deadlocks) {
  302. *deadlocks = lddmc_ref(lddmc_intersect(deadlocks_left, deadlocks_right));
  303. lddmc_deref(deadlocks_left);
  304. lddmc_deref(deadlocks_right);
  305. }
  306. return result;
  307. }
  308. }
  309. /* BFS strategy, sequential strategy (but operations are parallelized by Sylvan) */
  310. VOID_TASK_1(bfs, set_t, set)
  311. {
  312. MDD visited = set->mdd;
  313. MDD new = lddmc_ref(visited);
  314. size_t counter = 1;
  315. do {
  316. char buf[32];
  317. to_h(getCurrentRSS(), buf);
  318. printf("Memory usage: %s\n", buf);
  319. printf("Level %zu... ", counter++);
  320. if (report_levels) {
  321. printf("%zu states... ", (size_t)lddmc_satcount_cached(visited));
  322. }
  323. fflush(stdout);
  324. MDD cur = new;
  325. MDD deadlocks = cur;
  326. new = CALL(go_bfs, cur, visited, 0, next_count, check_deadlocks ? &deadlocks : NULL);
  327. lddmc_deref(cur);
  328. if (check_deadlocks) {
  329. printf("found %zu deadlock states... ", (size_t)lddmc_satcount_cached(deadlocks));
  330. if (deadlocks != lddmc_false) {
  331. printf("example: ");
  332. print_example(deadlocks);
  333. printf("... ");
  334. check_deadlocks = 0;
  335. }
  336. }
  337. // visited = visited + new
  338. MDD old_visited = visited;
  339. visited = lddmc_ref(lddmc_union(visited, new));
  340. lddmc_deref(old_visited);
  341. if (report_table) {
  342. size_t filled, total;
  343. sylvan_table_usage(&filled, &total);
  344. printf("done, table: %0.1f%% full (%zu nodes).\n", 100.0*(double)filled/total, filled);
  345. } else {
  346. printf("done.\n");
  347. }
  348. } while (new != lddmc_false);
  349. lddmc_deref(new);
  350. set->mdd = visited;
  351. }
  352. /* Obtain current wallclock time */
  353. static double
  354. wctime()
  355. {
  356. struct timeval tv;
  357. gettimeofday(&tv, NULL);
  358. return (tv.tv_sec + 1E-6 * tv.tv_usec);
  359. }
  360. int
  361. main(int argc, char **argv)
  362. {
  363. argp_parse(&argp, argc, argv, 0, 0, 0);
  364. FILE *f = fopen(model_filename, "r");
  365. if (f == NULL) {
  366. fprintf(stderr, "Cannot open file '%s'!\n", model_filename);
  367. return -1;
  368. }
  369. // Init Lace
  370. lace_init(workers, 1000000); // auto-detect number of workers, use a 1,000,000 size task queue
  371. lace_startup(0, NULL, NULL); // auto-detect program stack, do not use a callback for startup
  372. // Init Sylvan LDDmc
  373. // Nodes table size: 24 bytes * 2**N_nodes
  374. // Cache table size: 36 bytes * 2**N_cache
  375. // With: N_nodes=25, N_cache=24: 1.3 GB memory
  376. sylvan_init_package(1LL<<21, 1LL<<27, 1LL<<20, 1LL<<26);
  377. sylvan_init_ldd();
  378. // Read and report domain info (integers per vector and bits per integer)
  379. if (fread(&vector_size, sizeof(size_t), 1, f) != 1) Abort("Invalid input file!\n");
  380. printf("Vector size: %zu\n", vector_size);
  381. // Read initial state
  382. printf("Loading initial state... ");
  383. fflush(stdout);
  384. set_t states = set_load(f);
  385. printf("done.\n");
  386. // Read transitions
  387. if (fread(&next_count, sizeof(int), 1, f) != 1) Abort("Invalid input file!\n");
  388. next = (rel_t*)malloc(sizeof(rel_t) * next_count);
  389. printf("Loading transition relations... ");
  390. fflush(stdout);
  391. int i;
  392. for (i=0; i<next_count; i++) {
  393. next[i] = rel_load(f);
  394. printf("%d, ", i);
  395. fflush(stdout);
  396. }
  397. fclose(f);
  398. printf("done.\n");
  399. // Report statistics
  400. printf("Read file '%s'\n", argv[1]);
  401. printf("%zu integers per state, %d transition groups\n", vector_size, next_count);
  402. printf("MDD nodes:\n");
  403. printf("Initial states: %zu MDD nodes\n", lddmc_nodecount(states->mdd));
  404. for (i=0; i<next_count; i++) {
  405. printf("Transition %d: %zu MDD nodes\n", i, lddmc_nodecount(next[i]->mdd));
  406. }
  407. if (print_transition_matrix) {
  408. for (i=0; i<next_count; i++) {
  409. print_matrix(vector_size, next[i]->meta);
  410. printf(" (%d)\n", get_first(next[i]->meta));
  411. }
  412. }
  413. LACE_ME;
  414. #ifdef HAVE_PROFILER
  415. if (profile_filename != NULL) ProfilerStart(profile_filename);
  416. #endif
  417. if (strategy == 1) {
  418. double t1 = wctime();
  419. CALL(par, states);
  420. double t2 = wctime();
  421. printf("PAR Time: %f\n", t2-t1);
  422. } else {
  423. double t1 = wctime();
  424. CALL(bfs, states);
  425. double t2 = wctime();
  426. printf("BFS Time: %f\n", t2-t1);
  427. }
  428. #ifdef HAVE_PROFILER
  429. if (profile_filename != NULL) ProfilerStop();
  430. #endif
  431. // Now we just have states
  432. printf("Final states: %zu states\n", (size_t)lddmc_satcount_cached(states->mdd));
  433. printf("Final states: %zu MDD nodes\n", lddmc_nodecount(states->mdd));
  434. sylvan_stats_report(stdout, 1);
  435. return 0;
  436. }