Use microtime() for stopping the request loop. With this all threads run
for 10.000400 (+-0.000400), so using 10.0 seconds for computing the
stats is correct. Because the run time is practically the same in all
runs, we get more stable results for multiple runs.
Here are few example runs:
$ ./synch-parallel.sh
thread 4: finished OK in 10.000023 seconds
thread 1: finished OK in 10.000090 seconds
thread 0: finished OK in 10.000170 seconds
thread 2: finished OK in 10.000159 seconds
thread 5: finished OK in 10.000124 seconds
thread 3: finished OK in 10.000546 seconds
thread 6: finished OK in 10.000705 seconds
thread 7: finished OK in 10.000203 seconds
TLS: disabled
bytes sent: 6558744576 (655.874 Mbytes/s)
bytes received: 6567395328 (656.74 Mbytes/s)
I/O requests: 801156 (80115.6 IOPS)
$ ./synch-parallel.sh
thread 1: finished OK in 10.000013 seconds
thread 4: finished OK in 10.000083 seconds
thread 2: finished OK in 10.000007 seconds
thread 0: finished OK in 10.000046 seconds
thread 3: finished OK in 10.000080 seconds
thread 6: finished OK in 10.000013 seconds
thread 7: finished OK in 10.000052 seconds
thread 5: finished OK in 10.000266 seconds
TLS: disabled
bytes sent: 6436765696 (643.677 Mbytes/s)
bytes received: 6409781248 (640.978 Mbytes/s)
I/O requests: 784091 (78409.1 IOPS)
$ ./synch-parallel.sh
thread 1: finished OK in 10.000037 seconds
thread 0: finished OK in 10.000030 seconds
thread 7: finished OK in 10.000017 seconds
thread 2: finished OK in 10.000066 seconds
thread 5: finished OK in 10.000168 seconds
thread 4: finished OK in 10.000037 seconds
thread 6: finished OK in 10.000144 seconds
thread 3: finished OK in 10.000806 seconds
TLS: disabled
bytes sent: 6416236544 (641.624 Mbytes/s)
bytes received: 6434586624 (643.459 Mbytes/s)
I/O requests: 784352 (78435.2 IOPS)
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
tests/synch-parallel.c | 21 ++++++---------------
1 file changed, 6 insertions(+), 15 deletions(-)
diff --git a/tests/synch-parallel.c b/tests/synch-parallel.c
index 686e406..72402d7 100644
--- a/tests/synch-parallel.c
+++ b/tests/synch-parallel.c
@@ -43,98 +43,91 @@
/* We keep a shadow of the RAM disk so we can check integrity of the data. */
static char *ramdisk;
/* This is also defined in synch-parallel.sh and checked here. */
#define EXPORTSIZE (8*1024*1024)
/* How long (seconds) that the test will run for. */
#define RUN_TIME 10
/* Number of threads. */
#define NR_THREADS 8
#define MICROSECONDS 1000000
/* Unix socket. */
static const char *unixsocket;
struct thread_status {
size_t i; /* Thread index, 0 .. NR_THREADS-1 */
- time_t end_time; /* Threads run until this end time. */
uint64_t offset, length; /* Area assigned to this thread. */
int status; /* Return status. */
unsigned requests; /* Total number of requests made. */
uint64_t bytes_sent, bytes_received; /* Bytes sent and received by thread. */
};
static void *start_thread (void *arg);
static inline int64_t
microtime (void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * MICROSECONDS + tv.tv_usec;
}
int
main (int argc, char *argv[])
{
pthread_t threads[NR_THREADS];
struct thread_status status[NR_THREADS];
size_t i;
- time_t t;
int err;
unsigned requests, errors;
uint64_t bytes_sent, bytes_received;
if (argc != 2) {
fprintf (stderr, "%s socket\n", argv[0]);
exit (EXIT_FAILURE);
}
unixsocket = argv[1];
- /* Get the current time and the end time. */
- time (&t);
- t += RUN_TIME;
-
- srand (t + getpid ());
+ srand ((microtime () / MICROSECONDS) + getpid ());
/* Initialize the RAM disk with the initial data from
* nbdkit-pattern-filter.
*/
ramdisk = malloc (EXPORTSIZE);
if (ramdisk == NULL) {
perror ("calloc");
exit (EXIT_FAILURE);
}
for (i = 0; i < EXPORTSIZE; i += 8) {
uint64_t d = htobe64 (i);
memcpy (&ramdisk[i], &d, sizeof d);
}
/* Start the worker threads. */
for (i = 0; i < NR_THREADS; ++i) {
status[i].i = i;
- status[i].end_time = t;
status[i].offset = i * EXPORTSIZE / NR_THREADS;
status[i].length = EXPORTSIZE / NR_THREADS;
status[i].status = 0;
status[i].requests = 0;
status[i].bytes_sent = status[i].bytes_received = 0;
err = pthread_create (&threads[i], NULL, start_thread, &status[i]);
if (err != 0) {
errno = err;
perror ("pthread_create");
exit (EXIT_FAILURE);
}
}
/* Wait for the threads to exit. */
errors = 0;
requests = 0;
bytes_sent = bytes_received = 0;
for (i = 0; i < NR_THREADS; ++i) {
err = pthread_join (threads[i], NULL);
if (err != 0) {
@@ -167,42 +160,41 @@ main (int argc, char *argv[])
bytes_sent, (double) bytes_sent / RUN_TIME / 1000000);
printf ("bytes received: %" PRIu64 " (%g Mbytes/s)\n",
bytes_received, (double) bytes_received / RUN_TIME / 1000000);
printf ("I/O requests: %u (%g IOPS)\n",
requests, (double) requests / RUN_TIME);
exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
#define BUFFER_SIZE 16384
static void *
start_thread (void *arg)
{
struct thread_status *status = arg;
struct nbd_handle *nbd;
char *buf;
int cmd;
uint64_t offset;
- time_t t;
- int64_t start_usec, run_usec;
+ int64_t start_usec, stop_usec, now_usec;
buf = calloc (BUFFER_SIZE, 1);
if (buf == NULL) {
perror ("calloc");
exit (EXIT_FAILURE);
}
nbd = nbd_create ();
if (nbd == NULL) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
}
#ifdef TLS
/* Require TLS on the handle and fail if not available or if the
* handshake fails.
*/
if (nbd_set_tls (nbd, LIBNBD_TLS_REQUIRE) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
@@ -213,77 +205,76 @@ start_thread (void *arg)
exit (EXIT_FAILURE);
}
if (nbd_set_tls_psk_file (nbd, "keys.psk") == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
}
#endif
/* Connect to nbdkit. */
if (nbd_connect_unix (nbd, unixsocket) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
exit (EXIT_FAILURE);
}
assert (nbd_get_size (nbd) == EXPORTSIZE);
assert (nbd_can_multi_conn (nbd) > 0);
assert (nbd_is_read_only (nbd) == 0);
start_usec = microtime ();
+ stop_usec = start_usec + RUN_TIME * MICROSECONDS;
/* Issue commands. */
while (1) {
/* Run until the timer expires. */
- time (&t);
- if (t > status->end_time)
+ now_usec = microtime ();
+ if (now_usec >= stop_usec)
break;
/* Issue a synchronous read or write command. */
offset = status->offset + (rand () % (status->length - BUFFER_SIZE));
cmd = rand () & 1;
if (cmd == 0) {
if (nbd_pwrite (nbd, buf, BUFFER_SIZE, offset, 0) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
goto error;
}
status->bytes_sent += BUFFER_SIZE;
memcpy (&ramdisk[offset], buf, BUFFER_SIZE);
}
else {
if (nbd_pread (nbd, buf, BUFFER_SIZE, offset, 0) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
goto error;
}
status->bytes_received += BUFFER_SIZE;
if (memcmp (&ramdisk[offset], buf, BUFFER_SIZE) != 0) {
fprintf (stderr, "thread %zu: DATA INTEGRITY ERROR!\n", status->i);
goto error;
}
}
status->requests++;
}
- run_usec = microtime () - start_usec;
-
printf ("thread %zu: finished OK in %.6f seconds\n",
- status->i, (double) run_usec / MICROSECONDS);
+ status->i, (double) (now_usec - start_usec) / MICROSECONDS);
if (nbd_shutdown (nbd, 0) == -1) {
fprintf (stderr, "%s\n", nbd_get_error ());
goto error;
}
nbd_close (nbd);
free (buf);
status->status = 0;
pthread_exit (status);
error:
free (buf);
fprintf (stderr, "thread %zu: failed\n", status->i);
status->status = -1;
pthread_exit (status);
}
--
2.31.1