source: tests/PRNG.cfa @ 930a800

Last change on this file since 930a800 was 7d25f44, checked in by Peter A. Buhr <pabuhr@…>, 18 months ago

update files from old separator manipulator names to new names

  • Property mode set to 100644
File size: 7.3 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2021 University of Waterloo
3//
4// PRNG.c -- high-perforamnce pseudo-random numbers
5//
6// The contents of this file are covered under the licence agreement in the
7// file "LICENCE" distributed with Cforall.
8//
9// Author           : Peter A. Buhr
10// Created On       : Wed Dec 29 09:38:12 2021
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Thu Jun 29 10:18:35 2023
13// Update Count     : 424
14//
15
16#include <fstream.hfa>                                                                  // sout
17#include <stdlib.hfa>                                                                   // PRNG
18#include <clock.hfa>
19#include <limits.hfa>                                                                   // MAX
20#include <math.hfa>                                                                             // sqrt
21#include <malloc.h>                                                                             // malloc_stats
22#include <locale.h>                                                                             // setlocale
23#include <thread.hfa>
24#include <mutex_stmt.hfa>
25
26#define xstr(s) str(s)
27#define str(s) #s
28
29#if defined( __x86_64__ ) || defined( __aarch64__ )             // 64-bit architecture
30#define PRNG PRNG64
31#else                                                                                                   // 32-bit architecture
32#define PRNG PRNG32
33#endif // __x86_64__
34
35//#define TIME
36
37#ifdef TIME                                                                                             // use -O2 -nodebug
38#define STARTTIME start = timeHiRes()
39#define ENDTIME( extra ) sout | wd(0,1, (timeHiRes() - start)`ms / 1000.) | extra "seconds"
40enum { BUCKETS = 100_000, TRIALS = 1_000_000_000 };
41#else
42#define STARTTIME
43#define ENDTIME( extra )
44enum { BUCKETS = 100_000, TRIALS = 100_000_000 };
45#endif // TIME
46
47static void avgstd( size_t trials, size_t buckets[] ) {
48        size_t min = MAX, max = 0;
49        double sum = 0.0, diff;
50        for ( i; BUCKETS ) {
51                if ( buckets[i] < min ) min = buckets[i];
52                if ( buckets[i] > max ) max = buckets[i];
53                sum += buckets[i];
54        } // for
55
56        double avg = sum / BUCKETS;                                                     // average
57        sum = 0.0;
58        for ( i; BUCKETS ) {                                                            // sum squared differences from average
59                diff = buckets[i] - avg;
60                sum += diff * diff;
61        } // for
62        double std = sqrt( sum / BUCKETS );
63        mutex( sout ) sout | "trials"  | trials | "buckets" | BUCKETS
64                | "min" | min | "max" | max
65                | "avg" | wd(0,1, avg) | "std" | wd(0,1, std) | "rstd" | wd(0,1, (avg == 0 ? 0.0 : std / avg * 100)) | "%";
66} // avgstd
67
68
69size_t seed = 1009;
70
71thread T1 {};
72void main( T1 & ) {
73        size_t * buckets = calloc( BUCKETS );                           // too big for task stack
74        for ( TRIALS / 50 ) {
75                buckets[rand() % BUCKETS] += 1;                                 // concurrent
76        } // for
77        avgstd( TRIALS / 50, buckets );
78        free( buckets );
79} // main
80
81thread T2 {};
82void main( T2 & ) {
83        PRNG prng;
84        if ( seed != 0 ) set_seed( prng, seed );
85        size_t * buckets = calloc( BUCKETS );                           // too big for task stack
86        for ( TRIALS ) {
87                buckets[prng( prng ) % BUCKETS] += 1;                   // concurrent
88        } // for
89        avgstd( TRIALS, buckets );
90        free( buckets );
91} // main
92
93thread T3 {};
94void main( T3 & th ) {
95        size_t * buckets = calloc( BUCKETS );                           // too big for task stack
96        for ( TRIALS / 5 ) {
97                buckets[prng() % BUCKETS] += 1;                                 // concurrent
98        } // for
99        avgstd( TRIALS / 5, buckets );
100        free( buckets );
101} // main
102
103thread T4 {};
104void main( T4 & th ) {
105        size_t * buckets = calloc( BUCKETS );                           // too big for task stack
106        for ( TRIALS ) {
107                buckets[prng( th ) % BUCKETS] += 1;                             // concurrent
108        } // for
109        avgstd( TRIALS, buckets );
110        free( buckets );
111} // main
112
113// Compiler bug requires hiding declaration of th from the bucket access, otherwise the compiler thinks th is aliased
114// and continually reloads it from memory, which doubles the cost.
115static void dummy( thread$ & th ) __attribute__(( noinline ));
116static void dummy( thread$ & th ) {
117        size_t * buckets = (size_t *)calloc( BUCKETS, sizeof(size_t) ); // too big for task stack
118        for ( size_t i = 0; i < TRIALS; i += 1 ) {
119                buckets[prng( th ) % BUCKETS] += 1;                             // sequential
120        } // for
121        avgstd( TRIALS, buckets );
122        free( buckets );
123} // dummy
124
125
126int main() {
127        // setlocale( LC_NUMERIC, getenv( "LANG" ) );           // causes leaked storage message
128
129        // only works on the current pthread thread
130        // locale_t loc = newlocale( LC_NUMERIC_MASK, getenv( "LANG" ), (locale_t)0p );
131        // if ( loc == (locale_t)0p ) abort( "newlocale" );
132        // uselocale( loc );
133
134        enum { TASKS = 4 };
135        Time start;
136
137#ifdef TIME                                                                                             // too slow for test and generates non-repeatable results
138#if 1
139        sout | "glib rand" | nl | nl;
140
141        size_t rseed;
142        if ( seed != 0 ) rseed = seed;
143        else rseed = rdtscl();
144        srand( rseed );
145
146        sout | sepOff;
147        sout | nl | wd(26, "rand()" ) | wd(12, "rand(5)") | wd(12, "rand(0,5)" );
148        for ( 20 ) {
149                sout | wd(26, rand()) | nonl;
150                sout | wd(12, rand() % 5) | nonl;
151                sout | wd(12, rand() % (5 - 0 + 1) + 0);
152        } // for
153        sout | sepOn;
154        sout | "seed" | rseed;
155
156        sout | nl | "Sequential";
157        STARTTIME;
158        {
159                size_t * buckets = calloc( BUCKETS );                   // too big for task stack
160                for ( i; TRIALS / 5 ) {
161                        buckets[rand() % BUCKETS] += 1;                         // sequential
162                } // for
163                avgstd( TRIALS / 5, buckets );
164                free( buckets );
165        }
166        ENDTIME( " x 5 " );
167
168        sout | nl | "Concurrent";
169        STARTTIME;
170        {
171                processor p[TASKS - 1];                                                 // already 1 processor
172                {
173                        T1 t[TASKS];
174                } // wait for threads to complete
175        }
176        ENDTIME( " x 50 " );
177#endif // 0
178#endif // TIME
179
180        sout | nl | "CFA " xstr(PRNG_NAME);
181
182#if 1
183        PRNG prng;
184
185        if ( seed != 0 ) set_seed( prng, seed );
186
187        sout | sepOff;
188        sout | nl | wd(26, "PRNG()" ) | wd(12, "PRNG(5)") | wd(12, "PRNG(0,5)" );
189        for ( 20 ) {
190                sout | wd(26, prng( prng )) | nonl;                             // cascading => side-effect functions called in arbitary order
191                sout | wd(12, prng( prng, 5 )) | nonl;
192                sout | wd(12, prng( prng, 0, 5 ));
193        } // for
194        sout | sepOn;
195        sout | "seed" | get_seed( prng );
196
197        sout | nl | "Sequential";
198        STARTTIME;
199        {
200                size_t * buckets = calloc( BUCKETS );                   // too big for task stack
201                for ( TRIALS ) {
202                        buckets[prng( prng ) % BUCKETS] += 1;           // sequential
203                } // for
204                avgstd( TRIALS, buckets );
205                free( buckets );
206        }
207        ENDTIME();
208
209        sout | nl | "Concurrent";
210        STARTTIME;
211        {
212                processor p[TASKS - 1];                                                 // already 1 processor
213                {
214                        T2 t[TASKS];
215                } // wait for threads to complete
216        }
217        ENDTIME();
218#endif // 0
219#if 1
220        if ( seed != 0 ) set_seed( seed );
221
222        sout | sepOff;
223        sout | nl | wd(26, "prng()" ) | wd(12, "prng(5)") | wd(12, "prng(0,5)" );
224        for ( 20 ) {
225                sout | wd(26, prng()) | nonl;                                   // cascading => side-effect functions called in arbitary order
226                sout | wd(12, prng( 5 )) | nonl;
227                sout | wd(12, prng( 0, 5 ));
228        } // for
229        sout | sepOn;
230        sout | "seed" | get_seed( prng );
231
232        sout | nl | "Sequential";
233        STARTTIME;
234        {
235                size_t * buckets = calloc( BUCKETS );                   // too big for task stack
236                for ( TRIALS / 5 ) {
237                        buckets[prng() % BUCKETS] += 1;
238                } // for
239                avgstd( TRIALS / 5, buckets );
240                free( buckets );
241        }
242        ENDTIME( " x 5 " );
243
244        sout | nl | "Concurrent";
245        STARTTIME;
246        {
247                processor p[TASKS - 1];                                                 // already 1 processor
248                {
249                        T3 t[TASKS];
250                } // wait for threads to complete
251        }
252        ENDTIME( " x 5 " );
253#endif // 0
254#if 1
255        if ( seed != 0 ) set_seed( seed );
256        thread$ & th = *active_thread();
257
258        sout | sepOff;
259        sout | nl | wd(26, "prng(t)" ) | wd(12, "prng(t,5)") | wd(12, "prng(t,0,5)" );
260        for ( 20 ) {
261                sout | wd(26, prng( th )) | nonl;                               // cascading => side-effect functions called in arbitary order
262                sout | wd(12, prng( th, 5 )) | nonl;
263                sout | wd(12, prng( th, 0, 5 ));
264        } // for
265        sout | sepOn;
266        sout | "seed" | get_seed( prng );
267
268        sout | nl | "Sequential";
269        STARTTIME;
270        {
271                dummy( th );
272        }
273        ENDTIME();
274
275        sout | nl | "Concurrent";
276        STARTTIME;
277        {
278                processor p[TASKS - 1];                                                 // already 1 processor
279                {
280                        T4 t[TASKS];
281                } // wait for threads to complete
282        }
283        ENDTIME();
284#endif // 0
285//      malloc_stats();
286        // freelocale( loc );
287} // main
288
289
290// Local Variables: //
291// compile-command: "cfa -DTIME -O2 -nodebug PRNG.cfa" //
292// End: //
Note: See TracBrowser for help on using the repository browser.