|
Packit |
fe9d6e |
/*
|
|
Packit |
fe9d6e |
* Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
|
|
Packit |
fe9d6e |
*
|
|
Packit |
fe9d6e |
* This file may be redistributed and/or modified under the
|
|
Packit |
fe9d6e |
* terms of the GNU General Public License as published by the Free Software
|
|
Packit |
fe9d6e |
* Foundation; either version 2, or (at your option) any later version.
|
|
Packit |
fe9d6e |
*
|
|
Packit |
fe9d6e |
* It is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
Packit |
fe9d6e |
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
Packit |
fe9d6e |
* FOR A PARTICULAR PURPOSE. See the GNU General Public License in the
|
|
Packit |
fe9d6e |
* file COPYING for more details.
|
|
Packit |
fe9d6e |
*/
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#if defined(HAVE_CONFIG_H)
|
|
Packit |
fe9d6e |
# include "config.h"
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#include <stdio.h>
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#if defined(__vxworks)
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
int main(void)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
printf("test skipped\n");
|
|
Packit |
fe9d6e |
return 0;
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#else
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#if ((defined(_WIN32) && !defined(__CYGWIN32__) && !defined(__CYGWIN__)) \
|
|
Packit |
fe9d6e |
|| defined(_MSC_VER) || defined(_WIN32_WINCE)) \
|
|
Packit |
fe9d6e |
&& !defined(AO_USE_WIN32_PTHREADS)
|
|
Packit |
fe9d6e |
# define USE_WINTHREADS
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifdef USE_WINTHREADS
|
|
Packit |
fe9d6e |
# include <windows.h>
|
|
Packit |
fe9d6e |
#else
|
|
Packit |
fe9d6e |
# include <pthread.h>
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#include <stdlib.h>
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#include "atomic_ops_stack.h" /* includes atomic_ops.h as well */
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#if (defined(_WIN32_WCE) || defined(__MINGW32CE__)) && !defined(AO_HAVE_abort)
|
|
Packit |
fe9d6e |
# define abort() _exit(-1) /* there is no abort() in WinCE */
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifndef MAX_NTHREADS
|
|
Packit |
fe9d6e |
# define MAX_NTHREADS 100
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifndef DEFAULT_NTHREADS
|
|
Packit |
fe9d6e |
# define DEFAULT_NTHREADS 16 /* must be <= MAX_NTHREADS */
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifdef NO_TIMES
|
|
Packit |
fe9d6e |
# define get_msecs() 0
|
|
Packit |
fe9d6e |
#elif (defined(USE_WINTHREADS) || defined(AO_USE_WIN32_PTHREADS)) \
|
|
Packit |
fe9d6e |
&& !defined(CPPCHECK)
|
|
Packit |
fe9d6e |
# include <sys/timeb.h>
|
|
Packit |
fe9d6e |
unsigned long get_msecs(void)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
struct timeb tb;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
ftime(&tb);
|
|
Packit |
fe9d6e |
return (unsigned long)tb.time * 1000 + tb.millitm;
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
#else /* Unix */
|
|
Packit |
fe9d6e |
# include <time.h>
|
|
Packit |
fe9d6e |
# include <sys/time.h>
|
|
Packit |
fe9d6e |
unsigned long get_msecs(void)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
struct timeval tv;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
gettimeofday(&tv, 0);
|
|
Packit |
fe9d6e |
return (unsigned long)tv.tv_sec * 1000 + tv.tv_usec/1000;
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
#endif /* !NO_TIMES */
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
typedef struct le {
|
|
Packit |
fe9d6e |
AO_t next;
|
|
Packit |
fe9d6e |
int data;
|
|
Packit |
fe9d6e |
} list_element;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
AO_stack_t the_list = AO_STACK_INITIALIZER;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
void add_elements(int n)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
list_element * le;
|
|
Packit |
fe9d6e |
if (n == 0) return;
|
|
Packit |
fe9d6e |
add_elements(n-1);
|
|
Packit |
fe9d6e |
le = malloc(sizeof(list_element));
|
|
Packit |
fe9d6e |
if (le == 0)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
fprintf(stderr, "Out of memory\n");
|
|
Packit |
fe9d6e |
exit(2);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
le -> data = n;
|
|
Packit |
fe9d6e |
AO_stack_push(&the_list, (AO_t *)le);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifdef VERBOSE
|
|
Packit |
fe9d6e |
void print_list(void)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
list_element *p;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
for (p = (list_element *)AO_REAL_HEAD_PTR(the_list);
|
|
Packit |
fe9d6e |
p != 0;
|
|
Packit |
fe9d6e |
p = (list_element *)AO_REAL_NEXT_PTR(p -> next))
|
|
Packit |
fe9d6e |
printf("%d\n", p -> data);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
#endif /* VERBOSE */
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
static char marks[MAX_NTHREADS * (MAX_NTHREADS + 1) / 2 + 1];
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
void check_list(int n)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
list_element *p;
|
|
Packit |
fe9d6e |
int i;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
for (i = 1; i <= n; ++i) marks[i] = 0;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
for (p = (list_element *)AO_REAL_HEAD_PTR(the_list);
|
|
Packit |
fe9d6e |
p != 0;
|
|
Packit |
fe9d6e |
p = (list_element *)AO_REAL_NEXT_PTR(p -> next))
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
i = p -> data;
|
|
Packit |
fe9d6e |
if (i > n || i <= 0)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
fprintf(stderr, "Found erroneous list element %d\n", i);
|
|
Packit |
fe9d6e |
abort();
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
if (marks[i] != 0)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
fprintf(stderr, "Found duplicate list element %d\n", i);
|
|
Packit |
fe9d6e |
abort();
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
marks[i] = 1;
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
for (i = 1; i <= n; ++i)
|
|
Packit |
fe9d6e |
if (marks[i] != 1)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
fprintf(stderr, "Missing list element %d\n", i);
|
|
Packit |
fe9d6e |
abort();
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
volatile AO_t ops_performed = 0;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifndef LIMIT
|
|
Packit |
fe9d6e |
/* Total number of push/pop ops in all threads per test. */
|
|
Packit |
fe9d6e |
# ifdef AO_USE_PTHREAD_DEFS
|
|
Packit |
fe9d6e |
# define LIMIT 20000
|
|
Packit |
fe9d6e |
# else
|
|
Packit |
fe9d6e |
# define LIMIT 1000000
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifdef AO_HAVE_fetch_and_add
|
|
Packit |
fe9d6e |
# define fetch_and_add(addr, val) AO_fetch_and_add(addr, val)
|
|
Packit |
fe9d6e |
#else
|
|
Packit |
fe9d6e |
/* Fake it. This is really quite unacceptable for timing */
|
|
Packit |
fe9d6e |
/* purposes. But as a correctness test, it should be OK. */
|
|
Packit |
fe9d6e |
AO_INLINE AO_t fetch_and_add(volatile AO_t * addr, AO_t val)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
AO_t result = AO_load(addr);
|
|
Packit |
fe9d6e |
AO_store(addr, result + val);
|
|
Packit |
fe9d6e |
return result;
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifdef USE_WINTHREADS
|
|
Packit |
fe9d6e |
DWORD WINAPI run_one_test(LPVOID arg)
|
|
Packit |
fe9d6e |
#else
|
|
Packit |
fe9d6e |
void * run_one_test(void * arg)
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
list_element * t[MAX_NTHREADS + 1];
|
|
Packit |
fe9d6e |
int index = (int)(size_t)arg;
|
|
Packit |
fe9d6e |
int i;
|
|
Packit |
fe9d6e |
# ifdef VERBOSE
|
|
Packit |
fe9d6e |
int j = 0;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
printf("starting thread %d\n", index);
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
while (fetch_and_add(&ops_performed, index + 1) + index + 1 < LIMIT)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
for (i = 0; i < index + 1; ++i)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
t[i] = (list_element *)AO_stack_pop(&the_list);
|
|
Packit |
fe9d6e |
if (0 == t[i])
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
fprintf(stderr, "FAILED\n");
|
|
Packit |
fe9d6e |
abort();
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
for (i = 0; i < index + 1; ++i)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
AO_stack_push(&the_list, (AO_t *)t[i]);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
# ifdef VERBOSE
|
|
Packit |
fe9d6e |
j += index + 1;
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
# ifdef VERBOSE
|
|
Packit |
fe9d6e |
printf("finished thread %d: %d total ops\n", index, j);
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
return 0;
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#ifndef N_EXPERIMENTS
|
|
Packit |
fe9d6e |
# define N_EXPERIMENTS 1
|
|
Packit |
fe9d6e |
#endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
unsigned long times[MAX_NTHREADS + 1][N_EXPERIMENTS];
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
int main(int argc, char **argv)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
int nthreads;
|
|
Packit |
fe9d6e |
int max_nthreads;
|
|
Packit |
fe9d6e |
int exper_n;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
if (1 == argc)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
max_nthreads = DEFAULT_NTHREADS;
|
|
Packit |
fe9d6e |
assert(max_nthreads <= MAX_NTHREADS);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
else if (2 == argc)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
max_nthreads = atoi(argv[1]);
|
|
Packit |
fe9d6e |
if (max_nthreads < 1 || max_nthreads > MAX_NTHREADS)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
fprintf(stderr, "Invalid max # of threads argument\n");
|
|
Packit |
fe9d6e |
exit(1);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
else
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
fprintf(stderr, "Usage: %s [max # of threads]\n", argv[0]);
|
|
Packit |
fe9d6e |
exit(1);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
for (exper_n = 0; exper_n < N_EXPERIMENTS; ++ exper_n)
|
|
Packit |
fe9d6e |
for (nthreads = 1; nthreads <= max_nthreads; ++nthreads)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
int i;
|
|
Packit |
fe9d6e |
# ifdef USE_WINTHREADS
|
|
Packit |
fe9d6e |
DWORD thread_id;
|
|
Packit |
fe9d6e |
HANDLE thread[MAX_NTHREADS];
|
|
Packit |
fe9d6e |
# else
|
|
Packit |
fe9d6e |
pthread_t thread[MAX_NTHREADS];
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
int list_length = nthreads*(nthreads+1)/2;
|
|
Packit |
fe9d6e |
unsigned long start_time;
|
|
Packit |
fe9d6e |
list_element * le;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
# ifdef VERBOSE
|
|
Packit |
fe9d6e |
printf("Before add_elements: exper_n=%d, nthreads=%d,"
|
|
Packit |
fe9d6e |
" max_nthreads=%d, list_length=%d\n",
|
|
Packit |
fe9d6e |
exper_n, nthreads, max_nthreads, list_length);
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
add_elements(list_length);
|
|
Packit |
fe9d6e |
# ifdef VERBOSE
|
|
Packit |
fe9d6e |
printf("Initial list (nthreads = %d):\n", nthreads);
|
|
Packit |
fe9d6e |
print_list();
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
ops_performed = 0;
|
|
Packit |
fe9d6e |
start_time = get_msecs();
|
|
Packit |
fe9d6e |
for (i = 1; i < nthreads; ++i) {
|
|
Packit |
fe9d6e |
int code;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
# ifdef USE_WINTHREADS
|
|
Packit |
fe9d6e |
thread[i] = CreateThread(NULL, 0, run_one_test, (LPVOID)(size_t)i,
|
|
Packit |
fe9d6e |
0, &thread_id);
|
|
Packit |
fe9d6e |
code = thread[i] != NULL ? 0 : (int)GetLastError();
|
|
Packit |
fe9d6e |
# else
|
|
Packit |
fe9d6e |
code = pthread_create(&thread[i], 0, run_one_test,
|
|
Packit |
fe9d6e |
(void *)(size_t)i);
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
if (code != 0) {
|
|
Packit |
fe9d6e |
fprintf(stderr, "Thread creation failed %u\n", (unsigned)code);
|
|
Packit |
fe9d6e |
exit(3);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
/* We use the main thread to run one test. This allows gprof */
|
|
Packit |
fe9d6e |
/* profiling to work, for example. */
|
|
Packit |
fe9d6e |
run_one_test(0);
|
|
Packit |
fe9d6e |
for (i = 1; i < nthreads; ++i) {
|
|
Packit |
fe9d6e |
int code;
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
# ifdef USE_WINTHREADS
|
|
Packit |
fe9d6e |
code = WaitForSingleObject(thread[i], INFINITE) == WAIT_OBJECT_0 ?
|
|
Packit |
fe9d6e |
0 : (int)GetLastError();
|
|
Packit |
fe9d6e |
# else
|
|
Packit |
fe9d6e |
code = pthread_join(thread[i], 0);
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
if (code != 0) {
|
|
Packit |
fe9d6e |
fprintf(stderr, "Thread join failed %u\n", (unsigned)code);
|
|
Packit |
fe9d6e |
abort();
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
times[nthreads][exper_n] = get_msecs() - start_time;
|
|
Packit |
fe9d6e |
# ifdef VERBOSE
|
|
Packit |
fe9d6e |
printf("nthreads=%d, time_ms=%lu\n",
|
|
Packit |
fe9d6e |
nthreads, times[nthreads][exper_n]);
|
|
Packit |
fe9d6e |
printf("final list (should be reordered initial list):\n");
|
|
Packit |
fe9d6e |
print_list();
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
check_list(list_length);
|
|
Packit |
fe9d6e |
while ((le = (list_element *)AO_stack_pop(&the_list)) != 0)
|
|
Packit |
fe9d6e |
free(le);
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
for (nthreads = 1; nthreads <= max_nthreads; ++nthreads)
|
|
Packit |
fe9d6e |
{
|
|
Packit |
fe9d6e |
# ifndef NO_TIMES
|
|
Packit |
fe9d6e |
unsigned long sum = 0;
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
printf("About %d pushes + %d pops in %d threads:",
|
|
Packit |
fe9d6e |
LIMIT, LIMIT, nthreads);
|
|
Packit |
fe9d6e |
# ifndef NO_TIMES
|
|
Packit |
fe9d6e |
for (exper_n = 0; exper_n < N_EXPERIMENTS; ++exper_n) {
|
|
Packit |
fe9d6e |
# ifdef VERBOSE
|
|
Packit |
fe9d6e |
printf(" [%lums]", times[nthreads][exper_n]);
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
sum += times[nthreads][exper_n];
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
printf(" %lu msecs\n", (sum + N_EXPERIMENTS/2)/N_EXPERIMENTS);
|
|
Packit |
fe9d6e |
# else
|
|
Packit |
fe9d6e |
printf(" completed\n");
|
|
Packit |
fe9d6e |
# endif
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
return 0;
|
|
Packit |
fe9d6e |
}
|
|
Packit |
fe9d6e |
|
|
Packit |
fe9d6e |
#endif
|