random123

Counter-based Random Number Generators
git clone git://git.meso-star.com/random123.git
Log | Files | Refs | README | LICENSE

time_thread.c (8950B)


      1 /*
      2 Copyright 2010-2011, D. E. Shaw Research.
      3 All rights reserved.
      4 
      5 Redistribution and use in source and binary forms, with or without
      6 modification, are permitted provided that the following conditions are
      7 met:
      8 
      9 * Redistributions of source code must retain the above copyright
     10   notice, this list of conditions, and the following disclaimer.
     11 
     12 * Redistributions in binary form must reproduce the above copyright
     13   notice, this list of conditions, and the following disclaimer in the
     14   documentation and/or other materials provided with the distribution.
     15 
     16 * Neither the name of D. E. Shaw Research nor the names of its
     17   contributors may be used to endorse or promote products derived from
     18   this software without specific prior written permission.
     19 
     20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 */
     32 /*
     33  * Pthreads test and timing harness for Random123 RNGs.
     34  * Uses macros and util_expandtpl.h to "templatize" over all the
     35  * different permutations of RNGs and NxW and R.
     36  */
     37 
     38 #include "util.h"
     39 #include <sys/stat.h>
     40 
     41 #include "Random123/philox.h"
     42 #include "Random123/threefry.h"
     43 #include "Random123/ars.h"
     44 #include "Random123/aes.h"
     45 
     46 #include "time_misc.h"
     47 #include "util_print.h"
     48 #include "util.h"
     49 #include <pthread.h>
     50 
     51 /*
     52  * Main thread initializes thread_info[i].started to zero, .tid to its own
     53  * pthread_self() as a placeholder.
     54  * Child #i sets thread_info[i].tid to pthread_self() and then
     55  * sets .started.  Only child #i ever writes to thread_info[i], and
     56  * does so only once.  So searching through thread_info is race-free
     57  * for get_global_id, since it only ever looking for its own pthread_self
     58  * anyway.  The write to started needs to be atomic.
     59  * This is all so that we can use the same kernel as OpenCL/CUDA.
     60  * Note that parent keeps its own copy of thread ids returned
     61  * by pthread_create in tids[] to avoid any races with thread_info.
     62  */
     63 typedef struct {
     64     int started; /* started == 1 means tid contains pthread_self() of thread */
     65     pthread_t tid;
     66 } ThreadInfo;
     67 static volatile ThreadInfo *thread_info; /* thread_id state, one per thread */ \
     68 static int thread_count = 12;
     69 
     70 /* Linear search should be fast enough for small thread count... */
     71 R123_STATIC_INLINE int get_global_id(int x)
     72 {
     73     int i;
     74     pthread_t me = pthread_self();
     75     (void)x; /* why is this an arg? */
     76     for (i = 0; i < thread_count; i++) {
     77 	if (thread_info[i].started && pthread_equal(me, thread_info[i].tid))
     78 	    return i;
     79     }
     80     fprintf(stderr, "could not find thread %lu\n", (unsigned long) me);
     81     pthread_exit(NULL);
     82 }
     83 
     84 #define KERNEL R123_STATIC_INLINE
     85 #include "time_random123.h"
     86 
     87 #define TEST_TPL(NAME, N, W, R) \
     88 typedef struct { \
     89     unsigned kcount; \
     90     NAME##N##x##W##_ukey_t ukey; \
     91     NAME##N##x##W##_ctr_t ctr; \
     92     NAME##N##x##W##_ctr_t *octrs; \
     93 } ThreadData_##NAME##N##x##W##_##R; \
     94 \
     95 typedef struct { \
     96     ThreadData_##NAME##N##x##W##_##R *tp; \
     97     volatile ThreadInfo *tip; \
     98 } ThreadArg_##NAME##N##x##W##_##R; \
     99 \
    100 /* thread_run is launched in a new thread by pthread_create */ \
    101 void *thread_run_##NAME##N##x##W##_##R(void *p) \
    102 { \
    103     ThreadArg_##NAME##N##x##W##_##R *ta = p; \
    104     ThreadData_##NAME##N##x##W##_##R *tp = ta->tp; \
    105     volatile ThreadInfo *tip = ta->tip; \
    106     /* store our thread id for use by get_global_info */ \
    107     tip->tid = pthread_self(); \
    108     tip->started = 1; \
    109     test_##NAME##N##x##W##_##R(tp->kcount, tp->ctr, tp->ukey, tp->octrs); \
    110     return tp; \
    111 }\
    112 void NAME##N##x##W##_##R(NAME##N##x##W##_ctr_t ctr, NAME##N##x##W##_ukey_t ukey, NAME##N##x##W##_ctr_t kactr, unsigned count, void* unused) \
    113 { \
    114     const char *kernelname = #NAME #N "x" #W "_" #R; \
    115     double cur_time; \
    116     int i, n, niterations = numtrials; /* we make niterations + 2 (warmup, overhead) calls to the kernel */ \
    117     double basetime = 0., dt = 0., mindt = 0.; \
    118     ThreadData_##NAME##N##x##W##_##R td; /* same for all threads */ \
    119     ThreadArg_##NAME##N##x##W##_##R *tap; /* args for thread_run, 1 per thread */ \
    120     pthread_t me = pthread_self(); /* parent thread id */ \
    121     pthread_t *tids; /* array of child thread ids */ \
    122     void *vp; /* return from join */ \
    123     (void)unused; /* suppress warning */                                \
    124     CHECKNOTZERO(thread_info = (ThreadInfo *) malloc(sizeof(thread_info[0])*thread_count)); \
    125     CHECKNOTZERO(tap = (ThreadArg_##NAME##N##x##W##_##R *) malloc(sizeof(tap[0])*thread_count)); \
    126     CHECKNOTZERO(tids = (pthread_t *) malloc(sizeof(tids[0])*thread_count)); \
    127     for (i = 0; i < thread_count; i++) { \
    128 	thread_info[i].started = 0; \
    129 	thread_info[i].tid = me; \
    130 	tap[i].tip = &thread_info[i]; \
    131 	tap[i].tp = &td; \
    132     } \
    133     CHECKNOTZERO(td.octrs = (NAME##N##x##W##_ctr_t *) malloc(sizeof(td.octrs[0])*thread_count)); \
    134     td.ukey = ukey; \
    135     td.ctr = ctr; \
    136     td.kcount = 0; \
    137     for (n = -2; n < niterations; n++) { \
    138 	if (n == -2) { \
    139 	    if (count == 0) { \
    140 		/* try to set a good guess for count */ \
    141 		count = 1000000; \
    142 		dprintf(("starting with count = %u\n", count)); \
    143 	    } \
    144 	    td.kcount = count; \
    145 	} else if (n == -1) { \
    146 	    /* use first iteration time to calibrate count to get approximately sec_per_trial */ \
    147 	    if (count > 1) { \
    148 		count = (unsigned)(count * sec_per_trial / dt); \
    149 		dprintf(("scaled count = %u\n", count)); \
    150 	    } \
    151 	    /* second iteration is to calculate overhead after warmup */ \
    152 	    td.kcount = 1; \
    153 	} else if (n == 0) { \
    154 	    int xj; \
    155 	    /* Check that we got the expected value */ \
    156 	    for (xj = 0; xj < N; xj++) { \
    157 		if (kactr.v[xj] != td.octrs[0].v[xj]) { \
    158 		    printf("%s mismatch: xj = %d, expected\n", kernelname, xj); \
    159 		    printline_##NAME##N##x##W##_##R(ukey, ctr, &kactr, 1); \
    160 		    printf("    but got\n"); \
    161 		    printline_##NAME##N##x##W##_##R(ukey, ctr, td.octrs, 1); \
    162                     if(!debug) exit(1);                                 \
    163                     else break;                                         \
    164 		} else { \
    165 		    dprintf(("%s matched word %d\n", kernelname, xj)); \
    166 		} \
    167 	    } \
    168 	    basetime = dt; \
    169 	    if (verbose) { \
    170 		dprintf(("%s %.3f secs\n", kernelname, basetime)); \
    171 		printline_##NAME##N##x##W##_##R(ukey, ctr, td.octrs, thread_count); \
    172 	    } \
    173 	    td.kcount = count + 1; \
    174 	} \
    175 	dprintf(("call function %s\n", kernelname)); \
    176 	(void)timer(&cur_time); \
    177 	for (i = 0; i < thread_count; i++) { \
    178 	    CHECKZERO(pthread_create(&tids[i], NULL, thread_run_##NAME##N##x##W##_##R, &tap[i])); \
    179 	    dprintf(("thread %d started\n", i)); \
    180 	} \
    181 	for (i = 0; i < thread_count; i++) { \
    182 	    CHECKZERO(pthread_join(tids[i], &vp)); \
    183 	    dprintf(("thread %d done\n", i)); \
    184 	} \
    185 	dt = timer(&cur_time); \
    186 	dprintf(("iteration %d took %.3f secs\n", n, dt)); \
    187 	ALLZEROS(td.octrs, thread_count, N); \
    188 	if (n == 0 || dt < mindt) mindt = dt; \
    189     } \
    190     if (count > 1) { \
    191 	double tpB = (mindt - basetime) / ( (td.kcount - 1.) * thread_count * (N * W / 8.) ); \
    192 	printf("%-17s %#5.3g GB/s %u B granularity (best %u in %.3f s - %.6f s)\n", \
    193 	       kernelname, 1e-9/tpB, \
    194 	       (unsigned)(N*W/8), td.kcount, mindt, basetime ); \
    195 	fflush(stdout); \
    196     } \
    197     free((void *) thread_info); \
    198     thread_info = NULL; \
    199     free(td.octrs); \
    200     free(tap); \
    201     free(tids); \
    202 }
    203 
    204 #include "util_expandtpl.h"
    205 
    206 int main(int argc, char **argv)
    207 {
    208     char *cp;
    209     unsigned count = 0;
    210     int keyctroffset = 0;
    211     void *infop = NULL;
    212     
    213     progname = argv[0];
    214     if (argc > 3|| (argv[1] && argv[1][0] == '-')) {
    215 	fprintf(stderr, "Usage: %s [COUNT]\n", progname);
    216 	exit(1);
    217     }
    218     if (argc > 1)
    219 	count = atoi(argv[1]);
    220     if ((cp = getenv("TIME_THREAD_NTHREADS")) != NULL) {
    221         thread_count = atoi(cp);
    222     }
    223     printf("Running with %d threads.  Try 'env TIME_THREAD_NTHREADS=N %s' to change it\n", thread_count, argv[0]);
    224 
    225     if ((cp = getenv("TIME_THREAD_VERBOSE")) != NULL) {
    226 	verbose = atoi(cp);
    227     }
    228     if ((cp = getenv("TIME_THREAD_DEBUG")) != NULL) {
    229 	debug = atoi(cp);
    230     }
    231     if ((cp = getenv("TIME_THREAD_OFFSET")) != NULL) {
    232 	keyctroffset = atoi(cp);
    233     }
    234     if ((cp = getenv("TIME_THREAD_NUMTRIALS")) != NULL) {
    235         numtrials = atoi(cp);
    236     }
    237     if ((cp = getenv("TIME_THREAD_SEC_PER_TRIAL")) != NULL) {
    238         sec_per_trial = atof(cp);
    239     }
    240 #   include "time_initkeyctr.h"
    241     return 0;
    242 }