This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC] Lock elision "test suite", first draft


The attached c source files comprise an ad-hoc test suite for the
pthread_mutex lock elision.  They are meant to measure performance
of certain basic and corner cases in the use of elided locks.
They are _not_ designed to be useful benchmarks.  Most of the test
algorithms have been already mentioned in the older thread "Lock
elision test results".

 * ets-t<n>*.c are the main files of the individual test cases.
   Each test file starts with a comment describing the test. 
 * ets-helpers.c is a source file with some shared code that is
   included by the ets-t<n>*.c files.  It's not necessary to
   compile this file directly.

Set the value PLATFORM_ALIGNMENT_BYTES in ets-helpers.c to the
alignment necessary for data to make sure that two different
pieces of data stored in memory cannot cause a transactional
read/write or write/write conflict.  That is, set it to the size
of cache lines on the target platform.

For compilation, something like this should work:

  $ gcc -O3 -o ets-t1a ets-t1a.c -lpthread

Each of the tests can be run against different versions of glibc,
e.g. an old version without elision support, 2.18 with elision
disabled, 2.18 with elision enabled etc.

Any comments and discussions are welcome.

Ciao

Dominik ^_^  ^_^

-- 

Dominik Vogt
IBM Germany
/* Lock elision test suite, test #1a.
 *
 * Check lock elision performance of the locking scheme
 *
 *   lock mutex A
 *   lock mutex B
 *   unlock mutex A
 *   waste a good amount time
 *   unlock mutex B
 *
 * with a concurrent thread that locks and unlocks mutex A in a loop.
 *
 * Rationale: Locking mutex A puts it into the thread's read set.  The long run
 * time before unlocking B causes a certain abort ratio.  Once enough aborts
 * have occured, either thread switches to locking the mutex.  The mutex is then
 * put into the thread's write set and causes the other thread to abort too.  In
 * total, both threads should end up with roughly the same number of aborts.
 *
 * Note: With bad timing and tuning parameters the threads might become
 * synchronized in a way that causes almost all transactions to abort.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_THREADS 2
#define NUM_PASSES 10000
#define NUM_PASSES_2 100000

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m1 = { PTHREAD_MUTEX_INITIALIZER };
ETS_ALIGNMENT ets_mutex_t m2 = { PTHREAD_MUTEX_INITIALIZER };

ETS_ALIGNMENT volatile ets_counter_t c[NUM_THREADS + 1];
ETS_ALIGNMENT volatile int t1_done = 0;
ETS_ALIGNMENT volatile long j;

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work_1(void *ptr)
{
	long i;

	ets_thread_startup_barrier_and_set_thread_num(NULL, __FUNCTION__, ptr);
	for (i = 0; i < NUM_PASSES; )
	{
		int rc;

		rc = pthread_mutex_lock(&m1.m);
		assert(rc == 0);
		rc = pthread_mutex_lock(&m2.m);
		assert(rc == 0);
		c[0].c++;
		rc = pthread_mutex_unlock(&m1.m);
		assert(rc == 0);
		c[1].c++;
		i++;
		for (j = 0; j < NUM_PASSES_2; )
		{
			j++;
		}
		rc = pthread_mutex_unlock(&m2.m);
		assert(rc == 0);
	}
	t1_done = 1;
	pthread_barrier_wait(&barrier);
	pthread_exit((void *)0);
}

static void *_thread_do_work_2(void *ptr)
{
	uint64_t thread_num;

	ets_thread_startup_barrier_and_set_thread_num(
		&thread_num, __FUNCTION__, ptr);
	ets_begin_test();
	while (t1_done == 0)
	{
		int rc;

		rc = pthread_mutex_lock(&m1.m);
		assert(rc == 0);
		c[thread_num + 1].c++;
		rc = pthread_mutex_unlock(&m1.m);
		assert(rc == 0);
	}
	pthread_barrier_wait(&barrier);
	ets_end_test();
	ets_print_test_result(c[thread_num + 1].c);
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(NUM_THREADS);
	ets_counters_init(c, NUM_THREADS + 1);
	{
		pthread_t threads[NUM_THREADS];
		uint64_t i;

		pthread_create(&threads[0], NULL, _thread_do_work_1, NULL);
		for (i = 1; i < NUM_THREADS; i++)
		{
			pthread_create(
				&threads[i], NULL, _thread_do_work_2,
				(void *)i);
		}
		ets_join_threads(threads, NUM_THREADS);
		fprintf(stderr, "c[2] %" PRIu64 "\n", c[2].c);
	}

	return 0;
}
/* Lock elision test suite, test #1b.
 *
 * Check lock elision performance of the locking scheme
 *
 *   lock mutex A
 *   lock mutex B
 *   unlock mutex A
 *   waste a good amount time
 *   unlock mutex B
 *
 * with a concurrent thread that locks and unlocks mutex A in a loop.
 *
 * Rationale: Locking mutex A puts it into the thread's read set.  The long run
 * time before unlocking B causes a certain abort ratio.  Once enough aborts
 * have occured, either thread switches to locking the mutex.  The mutex is then
 * put into the thread's write set and causes the other thread to abort too.  In
 * total, both threads should end up with roughly the same number of aborts.
 *
 * Note: With bad timing and tuning parameters the threads might become
 * synchronized in a way that causes almost all transactions to abort.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_THREADS 2
#define NUM_PASSES 10000
#define NUM_PASSES_2 100000

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m1 = { PTHREAD_MUTEX_INITIALIZER };
ETS_ALIGNMENT ets_mutex_t m2 = { PTHREAD_MUTEX_INITIALIZER };

ETS_ALIGNMENT volatile ets_counter_t c[NUM_THREADS + 1];
ETS_ALIGNMENT volatile int t1_done = 0;
ETS_ALIGNMENT volatile long j;

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work_1(void *ptr)
{
	long i;

	ets_thread_startup_barrier_and_set_thread_num(NULL, __FUNCTION__, ptr);
	for (i = 0; i < NUM_PASSES; )
	{
		int rc;

		rc = pthread_mutex_lock(&m1.m);
		assert(rc == 0);
		rc = pthread_mutex_lock(&m2.m);
		assert(rc == 0);
		c[0].c++;
		rc = pthread_mutex_unlock(&m1.m);
		assert(rc == 0);
		c[1].c++;
		i++;
		for (j = 0; j < NUM_PASSES_2; )
		{
			j++;
		}
		rc = pthread_mutex_unlock(&m2.m);
		assert(rc == 0);
	}
	t1_done = 1;
	pthread_barrier_wait(&barrier);
	pthread_exit((void *)0);
}

static void *_thread_do_work_2(void *ptr)
{
	uint64_t thread_num;

	ets_thread_startup_barrier_and_set_thread_num(
		&thread_num, __FUNCTION__, ptr);
	ets_begin_test();
	while (t1_done == 0)
	{
		int rc;

		rc = pthread_mutex_lock(&m1.m);
		assert(rc == 0);
		c[0].c++;
		rc = pthread_mutex_unlock(&m1.m);
		assert(rc == 0);
	}
	pthread_barrier_wait(&barrier);
	ets_end_test();
	ets_print_test_result(c[thread_num + 1].c);
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(NUM_THREADS);
	ets_counters_init(c, NUM_THREADS + 1);
	{
		pthread_t threads[NUM_THREADS];
		uint64_t i;

		pthread_create(&threads[0], NULL, _thread_do_work_1, NULL);
		for (i = 1; i < NUM_THREADS; i++)
		{
			pthread_create(
				&threads[i], NULL, _thread_do_work_2,
				(void *)i);
		}
		ets_join_threads(threads, NUM_THREADS);
		fprintf(stderr, "c[2] %" PRIu64 "\n", c[2].c);
	}

	return 0;
}
/* Lock elision test suite, test #2.
 *
 * Check lock elision performance of the locking scheme
 *
 *   lock mutex 1
 *   lock mutex 2
 *   ...
 *   lock mutex <n>
 *   unlock mutex <n>
 *   ...
 *   unlock mutex 2
 *   unlock mutex 1
 *
 * in <m> parallel threads with contention on data in all levels of locking.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_THREADS 3
#define NUM_MUTEXES 10
#define NUM_PASSES 1000000

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m[NUM_MUTEXES];
ETS_ALIGNMENT volatile ets_counter_t c[NUM_MUTEXES];

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work(void *ptr)
{
	long i;
	uint64_t thread_num;

	ets_thread_startup_barrier_and_set_thread_num(
		&thread_num, __FUNCTION__, ptr);
	if (thread_num == 0)
	{
		ets_begin_test();
	}
	for (i = 0; i < NUM_PASSES; i++)
	{
		unsigned l;

		for (l = 0; l < NUM_MUTEXES; l++)
		{
			pthread_mutex_lock(&m[l].m);
			c[l].c++;
		}
		for (l = NUM_MUTEXES; l-- > 0; )
		{
			pthread_mutex_unlock(&m[l].m);
		}
	}
	pthread_barrier_wait(&barrier);
	if (thread_num == 0)
	{
		ets_end_test();
		ets_print_test_result(NUM_PASSES);
	}
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(NUM_THREADS);
	ets_mutexes_init(m, NUM_MUTEXES);
	ets_counters_init(c, NUM_MUTEXES);
	{
		pthread_t tids[NUM_THREADS];
		uint64_t i;

		for (i = 0; i < NUM_THREADS; i++)
		{
			pthread_create(
				&tids[i], NULL, _thread_do_work, (void *)i);
		}
		ets_join_threads(tids, NUM_THREADS);
	}

	return 0;
}
/* Lock elision test suite, test #3 (cache line pingpong).
 *
 * Check lock elision performance of the locking scheme
 *
 *   lock mutex 1
 *   unlock mutex 1
 *
 * in <m> parallel threads without contention on data.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_PASSES 10000000
#define NUM_THREADS 4

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m;

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work(void *ptr)
{
	long i;
	uint64_t thread_num;

	ets_thread_startup_barrier_and_set_thread_num(
		&thread_num, __FUNCTION__, ptr);
	if (thread_num == 0)
	{
		ets_begin_test();
	}
	for (i = 0; i < NUM_PASSES; )
	{
		pthread_mutex_lock(&m.m);
		i++;
		pthread_mutex_unlock(&m.m);
	}
	pthread_barrier_wait(&barrier);
	if (thread_num == 0)
	{
		ets_end_test();
		ets_print_test_result(NUM_PASSES);
	}
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(2);
	{
		pthread_t tids[NUM_THREADS];
		uint64_t i;

		for (i = 0; i < NUM_THREADS; i++)
		{
			pthread_create(
				&tids[i], NULL, _thread_do_work, (void *)i);
		}
		ets_join_threads(tids, NUM_THREADS);
	}

	return 0;
}
/* Lock elision test suite, test #4a (mutex for writing, unprotected read)
 *
 * Rationale: The mutex is used for writing with parralel unprotected read loops
 * at maximum speed.  This test checks the impact of non-transactional reads on
 * data that is in the transactions' write sets.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_THREADS 4
#define NUM_PASSES 10000000

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m1 = { PTHREAD_MUTEX_INITIALIZER };

ETS_ALIGNMENT volatile ets_counter_t c[NUM_THREADS];
ETS_ALIGNMENT volatile int t0_done = 0;
ETS_ALIGNMENT volatile long j;

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work_1(void *ptr)
{
	long i;

	ets_thread_startup_barrier_and_set_thread_num(NULL, __FUNCTION__, ptr);
	ets_begin_test();
	for (i = 0; i < NUM_PASSES; i++)
	{
		pthread_mutex_lock(&m1.m);
		c[0].c++;
		pthread_mutex_unlock(&m1.m);
	}
	t0_done = 1;
	pthread_barrier_wait(&barrier);
	ets_end_test();
	ets_print_test_result(NUM_PASSES);
	pthread_exit((void *)0);
}

static void *_thread_do_work_2(void *ptr)
{
	uint64_t thread_num;

	ets_thread_startup_barrier_and_set_thread_num(
		&thread_num, __FUNCTION__, ptr);
	while (t0_done == 0)
	{
		((volatile ets_counter_t *)c)[thread_num].c = c[0].c;
	}
	pthread_barrier_wait(&barrier);
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(NUM_THREADS);
	ets_counters_init(c, NUM_THREADS);
	{
		pthread_t tids[NUM_THREADS];
		uint64_t i;

		pthread_create(&tids[0], NULL, _thread_do_work_1, NULL);
		for (i = 1; i < NUM_THREADS; i++)
		{
			pthread_create(
				&tids[i], NULL, _thread_do_work_2, (void *)i);
		}
		ets_join_threads(tids, NUM_THREADS);
	}

	return 0;
}
/* Lock elision test suite, test #4b (mutex for reading, unprotected read)
 *
 * Rationale: The mutex is used for reading with parralel unprotected read loops
 * at maximum speed.  This test checks the impact of non-transactional reads on
 * data that is in the transactions' read sets.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_THREADS 4
#define NUM_PASSES 10000000

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m1 = { PTHREAD_MUTEX_INITIALIZER };

ETS_ALIGNMENT volatile ets_counter_t c[1];
ETS_ALIGNMENT volatile int t0_done = 0;
ETS_ALIGNMENT volatile long j;

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work_1(void *ptr)
{
	long i;

	ets_thread_startup_barrier_and_set_thread_num(NULL, __FUNCTION__, ptr);
	ets_begin_test();
	for (i = 0; i < NUM_PASSES; i++)
	{
		volatile ets_counter_t tmp;

		pthread_mutex_lock(&m1.m);
		tmp = c[0];
		(void)tmp;
		pthread_mutex_unlock(&m1.m);
	}
	t0_done = 1;
	pthread_barrier_wait(&barrier);
	ets_end_test();
	ets_print_test_result(NUM_PASSES);
	pthread_exit((void *)0);
}

static void *_thread_do_work_2(void *ptr)
{
	uint64_t thread_num;

	ets_thread_startup_barrier_and_set_thread_num(
		&thread_num, __FUNCTION__, ptr);
	while (t0_done == 0)
	{
		volatile ets_counter_t tmp;

		tmp = c[0];
		(void)tmp;
	}
	pthread_barrier_wait(&barrier);
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(NUM_THREADS);
	ets_counters_init(c, 1);
	{
		pthread_t tids[NUM_THREADS];
		uint64_t i;

		pthread_create(&tids[0], NULL, _thread_do_work_1, NULL);
		for (i = 1; i < NUM_THREADS; i++)
		{
			pthread_create(
				&tids[i], NULL, _thread_do_work_2, (void *)i);
		}
		ets_join_threads(tids, NUM_THREADS);
	}

	return 0;
}
/* Lock elision test suite, test #4c (mutex for reading, unprotected write)
 *
 * Rationale: The mutex is used for reading with parralel unprotected write
 * loops at maximum speed.  This test checks the impact of non-transactional
 * writes on data that is in the transactions' read sets.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_THREADS 4
#define NUM_PASSES 10000000

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m1 = { PTHREAD_MUTEX_INITIALIZER };

ETS_ALIGNMENT volatile ets_counter_t c[1];
ETS_ALIGNMENT volatile int t0_done = 0;
ETS_ALIGNMENT volatile long j;

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work_1(void *ptr)
{
	long i;

	ets_thread_startup_barrier_and_set_thread_num(NULL, __FUNCTION__, ptr);
	ets_begin_test();
	for (i = 0; i < NUM_PASSES; i++)
	{
		volatile ets_counter_t tmp;

		pthread_mutex_lock(&m1.m);
		tmp = c[0];
		(void)tmp;
		pthread_mutex_unlock(&m1.m);
	}
	t0_done = 1;
	pthread_barrier_wait(&barrier);
	ets_end_test();
	ets_print_test_result(NUM_PASSES);
	pthread_exit((void *)0);
}

static void *_thread_do_work_2(void *ptr)
{
	uint64_t thread_num;

	ets_thread_startup_barrier_and_set_thread_num(
		&thread_num, __FUNCTION__, ptr);
	while (t0_done == 0)
	{
		c[0].c++;
	}
	pthread_barrier_wait(&barrier);
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(NUM_THREADS);
	ets_counters_init(c, 1);
	{
		pthread_t tids[NUM_THREADS];
		uint64_t i;

		pthread_create(&tids[0], NULL, _thread_do_work_1, NULL);
		for (i = 1; i < NUM_THREADS; i++)
		{
			pthread_create(
				&tids[i], NULL, _thread_do_work_2, (void *)i);
		}
		ets_join_threads(tids, NUM_THREADS);
	}

	return 0;
}
/* Lock elision test suite, test #5 (single thread)
 *
 * Rationale: Check single thread locking performance.
 */

/* ---------------------------- included header files ---------------------- */

/* ---------------------------- local definitions -------------------------- */

#define NUM_PASSES 100000000

/* ---------------------------- local macros ------------------------------- */

/* ---------------------------- included code files ------------------------ */

#include "ets-helpers.c"

/* ---------------------------- local types -------------------------------- */

ETS_ALIGNMENT ets_mutex_t m1 = { PTHREAD_MUTEX_INITIALIZER };
ETS_ALIGNMENT ets_counter_t c[1];

/* ---------------------------- local variables ---------------------------- */

/* ---------------------------- local functions ---------------------------- */

static void *_thread_do_work(void *ptr)
{
	long i;

	ets_thread_startup_barrier_and_set_thread_num(NULL, __FUNCTION__, ptr);
	ets_begin_test();
	for (i = 0; i < NUM_PASSES; i++)
	{
		pthread_mutex_lock(&m1.m);
		pthread_mutex_unlock(&m1.m);
	}
	ets_end_test();
	ets_print_test_result(NUM_PASSES);
	pthread_exit((void *)0);
}

/* ---------------------------- interface functions ------------------------ */

int main(int argc, char **argv)
{
	ets_barrier_init(1);
	ets_counters_init(c, 1);
	{
		pthread_t tids[1];

		pthread_create(&tids[0], NULL, _thread_do_work, NULL);
		ets_join_threads(tids, 1);
	}

	return 0;
}
/* Lock elision test suite helpers.
 *
 * This file is meant to be included from the source c file.
 */

/* ---------------------------- included header files ---------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <inttypes.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <assert.h>

/* ---------------------------- definitions -------------------------------- */

#define PLATFORM_ALIGNMENT_BYTES 256

/* ---------------------------- macros ------------------------------------- */

#if (PLATFORM_ALIGNMENT_BYTES > 0)
#define ETS_ALIGNMENT __attribute__ ((aligned(PLATFORM_ALIGNMENT_BYTES)))
#define ETS_PADDING(size) char pad[((PLATFORM_ALIGNMENT_BYTES) - (size))]
#else
#define ETS_ALIGNMENT
#define ETS_PADDING(size)
#endif

/* ---------------------------- types -------------------------------------- */

typedef struct
{
	pthread_mutex_t m;
	ETS_PADDING(sizeof(pthread_mutex_t));
} ets_mutex_t;

typedef struct
{
	uint64_t c;
	ETS_PADDING(sizeof(uint64_t));
} ets_counter_t;

/* ----------------------------  variables --------------------------------- */

ETS_ALIGNMENT struct
{
	struct timespec start;
	struct timespec end;
} ets_ts;

ETS_ALIGNMENT pthread_barrier_t barrier;

/* ---------------------------- functions ---------------------------------- */

void ets_barrier_init(unsigned num_threads)
{
	int rc;

	rc = pthread_barrier_init(&barrier, NULL, num_threads);
	if (rc != 0)
	{
		perror("pthread_barrier_init");
		exit(rc);
	}

	return;
}

void ets_mutexes_init(ets_mutex_t *mutexes, unsigned num_mutexes)
{
	unsigned i;

	for (i = 0; i < num_mutexes; i++)
	{
		int rc;

		rc = pthread_mutex_init(&mutexes[i].m, NULL);
		if (rc != 0)
		{
			perror("pthread_mutex_init");
			exit(rc);
		}
	}

	return;
}

void ets_counters_init(volatile ets_counter_t *counters, unsigned num_counters)
{
	unsigned i;

	for (i = 0; i < num_counters; i++)
	{
		counters[i].c = 0;
	}

	return;
}

void ets_thread_startup_barrier_and_set_thread_num(
	uint64_t *ret_thread_num, const char *function_name, void *thread_arg)
{
	pid_t tid;

	if (ret_thread_num != NULL)
	{
		*ret_thread_num = (uint64_t)thread_arg;
	}
	tid = syscall (SYS_gettid);
	fprintf(
		stderr, "%s: id %" PRIu64 " tid %" PRIu64 "\n", function_name,
		(unsigned long)(uint64_t)thread_arg, (unsigned long)tid);
	pthread_barrier_wait(&barrier);

	return;
}

void ets_join_threads(pthread_t *tids, unsigned num_threads)
{
	unsigned i;

	for (i = 0; i < num_threads; i++)
	{
		void *retval;

		pthread_join(tids[i], &retval);
	}

	return;
}

void ets_begin_test(void)
{
	int rc;

	rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ets_ts.start);
	if (rc != 0)
	{
		perror("clock_gettime");
		exit(1);
	}

	return;
}

void ets_end_test(void)
{
	int rc;

	rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ets_ts.end);
	if (rc != 0)
	{
		perror("clock_gettime");
		exit(1);
	}

	return;
}

void ets_print_test_result(long work_done)
{
	uint64_t ns;
	double ms;

	ns = (ets_ts.end.tv_sec - ets_ts.start.tv_sec) * 1000000000;
	ns += ets_ts.end.tv_nsec - ets_ts.start.tv_nsec;
	if (ets_ts.end.tv_nsec < ets_ts.start.tv_nsec)
	{
		ns += 1000000000;
	}
	ms = (double)ns / 1000000.0;
	printf("ms     : %f\n", ms);
	if (work_done > 0)
	{
		printf("work/ms: %f\n", (double)work_done / ms);
	}

	return;
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]