/* Generates about 1 MB of random data that is then divided into 16 chunks. A separate subprocess sorts each chunk; the subprocesses run in parallel. Then we merge the chunks and verify that the result is what it should be. */ #include "tests/vm/parallel-merge.h" #include #include #include "tests/arc4.h" #include "tests/lib.h" #include "tests/main.h" #define CHUNK_SIZE (128 * 1024) #define CHUNK_CNT 8 /* Number of chunks. */ #define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */ unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE]; size_t histogram[256]; /* Initialize buf1 with random data, then count the number of instances of each value within it. */ static void init (void) { struct arc4 arc4; size_t i; msg ("init"); arc4_init (&arc4, "foobar", 6); arc4_crypt (&arc4, buf1, sizeof buf1); for (i = 0; i < sizeof buf1; i++) histogram[buf1[i]]++; } /* Sort each chunk of buf1 using SUBPROCESS, which is expected to return EXIT_STATUS. */ static void sort_chunks (const char *subprocess, int exit_status) { pid_t children[CHUNK_CNT]; size_t i; for (i = 0; i < CHUNK_CNT; i++) { char fn[128]; char cmd[128]; int handle; msg ("sort chunk %zu", i); /* Write this chunk to a file. */ snprintf (fn, sizeof fn, "buf%zu", i); create (fn, CHUNK_SIZE); quiet = true; CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); close (handle); /* Sort with subprocess. */ snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn); CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd); quiet = false; } for (i = 0; i < CHUNK_CNT; i++) { char fn[128]; int handle; CHECK (wait (children[i]) == exit_status, "wait for child %zu", i); /* Read chunk back from file. */ quiet = true; snprintf (fn, sizeof fn, "buf%zu", i); CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); close (handle); quiet = false; } } /* Merge the sorted chunks in buf1 into a fully sorted buf2. */ static void merge (void) { unsigned char *mp[CHUNK_CNT]; size_t mp_left; unsigned char *op; size_t i; msg ("merge"); /* Initialize merge pointers. */ mp_left = CHUNK_CNT; for (i = 0; i < CHUNK_CNT; i++) mp[i] = buf1 + CHUNK_SIZE * i; /* Merge. */ op = buf2; while (mp_left > 0) { /* Find smallest value. */ size_t min = 0; for (i = 1; i < mp_left; i++) if (*mp[i] < *mp[min]) min = i; /* Append value to buf2. */ *op++ = *mp[min]; /* Advance merge pointer. Delete this chunk from the set if it's emptied. */ if ((++mp[min] - buf1) % CHUNK_SIZE == 0) mp[min] = mp[--mp_left]; } } static void verify (void) { size_t buf_idx; size_t hist_idx; msg ("verify"); buf_idx = 0; for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram; hist_idx++) { while (histogram[hist_idx]-- > 0) { if (buf2[buf_idx] != hist_idx) fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx); buf_idx++; } } msg ("success, buf_idx=%'zu", buf_idx); } void parallel_merge (const char *child_name, int exit_status) { init (); sort_chunks (child_name, exit_status); merge (); verify (); }