replace ::cast_dynamic() with relevant ActionManager::get_*_action() calls
[ardour.git] / tools / thread_readtest.cc
1 /* g++ -o thread_readtest thread_readtest.cc `pkg-config --cflags --libs glibmm-2.4` -lm */
2
3 #ifndef _WIN32
4 #  define HAVE_MMAP
5 #endif
6
7 #include <stdlib.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <unistd.h>
11 #include <stdint.h>
12 #include <string.h>
13 #include <getopt.h>
14 #include <fcntl.h>
15 #include <math.h>
16
17 #undef HAVE_MMAP
18 #ifdef HAVE_MMAP
19 #  include <sys/stat.h>
20 #  include <sys/mman.h>
21 #endif
22
23 #include <glibmm.h>
24
25 char* data = 0;
26
27 void
28 usage ()
29 {
30         fprintf (stderr, "thread_readtest [ -b BLOCKSIZE ] [ -l FILELIMIT] [ -n NTHREADS ] [ -D ] [ -R ] [ -M ] filename-template\n");
31 }
32
33 Glib::Threads::Cond pool_run;
34 Glib::Threads::Cond pool_done;
35 Glib::Threads::Mutex pool_lock;
36 std::list<int> pool_work;
37 std::vector<Glib::Threads::Thread*> thread_pool;
38 int pool_errors = 0;
39 bool thread_pool_lives = true;
40
41 struct ThreadData {
42         int id;
43         char *data;
44         size_t block_size;
45 };
46
47 void
48 thread_pool_work (ThreadData* td)
49 {
50         Glib::Threads::Mutex::Lock lm (pool_lock);
51
52         while (thread_pool_lives) {
53                 pool_run.wait (pool_lock);
54
55                 if (!thread_pool_lives) {
56                         return;
57                 }
58
59                 /* we're awake ... get some work */
60
61                 while (!pool_work.empty()) {
62
63                         int file_descriptor = pool_work.front ();
64                         pool_work.pop_front ();
65
66                         /* release the lock while we do work */
67
68                         lm.release ();
69
70                         /* do the work */
71
72                         int err = 0;
73
74                         ssize_t nread;
75
76                   again:
77                         if ((nread = ::read (file_descriptor, td->data, td->block_size)) != td->block_size) {
78                                 if (nread != 0 && errno == EAGAIN) {
79                                         fprintf (stderr, "read requires retry\n");
80                                         goto again;
81                                 }
82                                 if (nread != 0) {
83                                         fprintf (stderr, "thread %d has error = %s\n", td->id, strerror (errno));
84                                 }
85                                 err++;
86                         }
87
88                         /* reacquire lock so that we can check the status of
89                          * things and possibly wake the master.
90                          */
91
92                         lm.acquire ();
93                         pool_errors += err;
94
95                         if (pool_work.empty()) {
96                                 /* work is finished, tell the master */
97                                 pool_done.signal ();
98                         }
99                 }
100
101         }
102
103         return;
104 }
105
106 void
107 stop_thread_pool ()
108 {
109         {
110                 Glib::Threads::Mutex::Lock lm (pool_lock);
111                 thread_pool_lives = false;
112                 pool_work.clear ();
113                 pool_run.broadcast ();
114         }
115
116         /* XXX wait for each thread to finish */
117 }
118
119 void
120 build_thread_pool (int nthreads, size_t block_size)
121 {
122         for (int n = 0; n < nthreads; ++n) {
123                 ThreadData* td = new ThreadData;
124                 td->data = (char*) malloc (sizeof (char) * block_size);
125                 td->block_size = block_size;
126                 td->id = n;
127
128                 thread_pool.push_back (Glib::Threads::Thread::create (sigc::bind (sigc::ptr_fun (thread_pool_work), td)));
129         }
130 }
131
132 int
133 run_thread_pool (int* files, int nfiles)
134 {
135         Glib::Threads::Mutex::Lock lm (pool_lock);
136
137         /* Queue up all the files */
138         for (int n = 0; n < nfiles; ++n) {
139                 pool_work.push_back (files[n]);
140         }
141
142         pool_errors = 0;
143
144         /* wake everybody up */
145         pool_run.broadcast ();
146
147         /* wait for everyone to finish */
148
149         pool_done.wait (pool_lock);
150
151         if (pool_errors) {
152                 return -1;
153         }
154
155         return 0;
156 }
157
158 int
159 main (int argc, char* argv[])
160 {
161         int* files;
162         char optstring[] = "b:DRMl:q";
163         uint32_t block_size = 64 * 1024 * 4;
164         int max_files = -1;
165         int nthreads = 16;
166 #ifdef __APPLE__
167         int direct = 0;
168         int noreadahead = 0;
169 #endif
170 #ifdef HAVE_MMAP
171         int use_mmap = 0;
172         void  **addr;
173         size_t *flen;
174 #endif
175         const struct option longopts[] = {
176                 { "blocksize", 1, 0, 'b' },
177                 { "direct", 0, 0, 'D' },
178                 { "mmap", 0, 0, 'M' },
179                 { "noreadahead", 0, 0, 'R' },
180                 { "limit", 1, 0, 'l' },
181                 { "nthreads", 16, 0, 'n' },
182                 { 0, 0, 0, 0 }
183         };
184
185         int option_index = 0;
186         int c = 0;
187         char const * name_template = 0;
188         int flags = O_RDONLY;
189         int n = 0;
190         int nfiles = 0;
191         int quiet = 0;
192
193         while (1) {
194                 if ((c = getopt_long (argc, argv, optstring, longopts, &option_index)) == -1) {
195                         break;
196                 }
197
198                 switch (c) {
199                 case 'b':
200                         block_size = atoi (optarg);
201                         break;
202                 case 'l':
203                         max_files = atoi (optarg);
204                         break;
205                 case 'D':
206 #ifdef __APPLE__
207                         direct = 1;
208 #endif
209                         break;
210                 case 'M':
211 #ifdef HAVE_MMAP
212                         use_mmap = 1;
213 #endif
214                         break;
215                 case 'R':
216 #ifdef __APPLE__
217                         noreadahead = 1;
218 #endif
219                         break;
220                 case 'q':
221                         quiet = 1;
222                         break;
223                 case 'n':
224                         nthreads = atoi (optarg);
225                         break;
226                 default:
227                         usage ();
228                         return 0;
229                 }
230         }
231
232         if (optind < argc) {
233                 name_template = argv[optind];
234         } else {
235                 usage ();
236                 return 1;
237         }
238
239         while (1) {
240                 char path[PATH_MAX+1];
241
242                 snprintf (path, sizeof (path), name_template, n+1);
243
244                 if (access (path, R_OK) != 0) {
245                         break;
246                 }
247
248                 ++n;
249
250                 if (max_files > 0 &&  n >= max_files) {
251                         break;
252                 }
253         }
254
255         if (n == 0) {
256                 fprintf (stderr, "No matching files found for %s\n", name_template);
257                 return 1;
258         }
259
260         if (!quiet) {
261                 printf ("# Discovered %d files using %s\n", n, name_template);
262         }
263
264         nfiles = n;
265         files = (int *) malloc (sizeof (int) * nfiles);
266 #ifdef HAVE_MMAP
267         if (use_mmap) {
268                 if (!quiet) {
269                         printf ("# Using mmap().\n");
270                 }
271                 addr = malloc (sizeof (void*) * nfiles);
272                 flen = (size_t*) malloc (sizeof (size_t) * nfiles);
273         }
274 #endif
275
276         for (n = 0; n < nfiles; ++n) {
277
278                 char path[PATH_MAX+1];
279                 int fd;
280
281                 snprintf (path, sizeof (path), name_template, n+1);
282
283                 if ((fd = open (path, flags, 0644)) < 0) {
284                         fprintf (stderr, "Could not open file #%d @ %s (%s)\n", n, path, strerror (errno));
285                         return 1;
286                 }
287
288 #ifdef __APPLE__
289                 if (direct) {
290                         /* Apple man pages say only that it returns "a value other than -1 on success",
291                                  which probably means zero, but you just can't be too careful with
292                                  those guys.
293                                  */
294                         if (fcntl (fd, F_NOCACHE, 1) == -1) {
295                                 fprintf (stderr, "Cannot set F_NOCACHE on file #%d\n", n);
296                         }
297                 }
298
299                 if (noreadahead) {
300                         if (fcntl (fd, F_RDAHEAD, 0) == -1) {
301                                 fprintf (stderr, "Cannot set F_READAHED on file #%d\n", n);
302                         }
303                 }
304 #endif
305
306                 files[n] = fd;
307
308 #ifdef HAVE_MMAP
309                 if (use_mmap) {
310                         struct stat s;
311                         if (fstat (fd, & s)) {
312                                 fprintf (stderr, "Could not stat fd #%d @ %s\n", n, path);
313                                 return 1;
314                         }
315                         if (s.st_size < block_size) {
316                                 fprintf (stderr, "file is shorter than blocksize #%d @ %s\n", n, path);
317                                 return 1;
318                         }
319                         flen[n] = s.st_size;
320                         addr[n] = mmap (0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
321                         if (addr[n] == MAP_FAILED) {
322                                 fprintf (stderr, "Could not mmap file #%d @ %s (%s)\n", n, path, strerror (errno));
323                                 return 1;
324                         }
325                 }
326 #endif
327         }
328
329         data = (char*) malloc (sizeof (char) * block_size);
330         uint64_t _read = 0;
331         double max_elapsed = 0;
332         double total_time = 0;
333         double var_m = 0;
334         double var_s = 0;
335         uint64_t cnt = 0;
336
337         build_thread_pool (nthreads, block_size);
338
339         while (1) {
340                 gint64 before;
341                 before = g_get_monotonic_time();
342
343                 if (run_thread_pool (files, nfiles)) {
344                         fprintf (stderr, "thread pool error\n");
345                         goto out;
346                 }
347
348                 _read += block_size;
349                 gint64 elapsed = g_get_monotonic_time() - before;
350                 double bandwidth = ((nfiles * block_size)/1048576.0) / (elapsed/1000000.0);
351
352                 if (!quiet) {
353                         printf ("# BW @ %lu %.3f seconds bandwidth %.4f MB/sec\n", (long unsigned int)_read, elapsed/1000000.0, bandwidth);
354                 }
355
356                 total_time += elapsed;
357
358                 ++cnt;
359                 if (max_elapsed == 0) {
360                         var_m = elapsed;
361                 } else {
362                         const double var_m1 = var_m;
363                         var_m = var_m + (elapsed - var_m) / (double)(cnt);
364                         var_s = var_s + (elapsed - var_m) * (elapsed - var_m1);
365                 }
366
367                 if (elapsed > max_elapsed) {
368                         max_elapsed = elapsed;
369                 }
370
371         }
372
373 out:
374         if (max_elapsed > 0 && total_time > 0) {
375                 double stddev = cnt > 1 ? sqrt(var_s / ((double)(cnt-1))) : 0;
376                 double bandwidth = ((nfiles * _read)/1048576.0) / (total_time/1000000.0);
377                 double min_throughput = ((nfiles * block_size)/1048576.0) / (max_elapsed/1000000.0);
378                 printf ("# Min: %.4f MB/sec Avg: %.4f MB/sec  || Max: %.3f sec \n", min_throughput, bandwidth, max_elapsed/1000000.0);
379                 printf ("# Max Track count: %d @ 48000SPS\n", (int) floor(1048576.0 * bandwidth / (4 * 48000.)));
380                 printf ("# Sus Track count: %d @ 48000SPS\n", (int) floor(1048576.0 * min_throughput / (4 * 48000.)));
381                 printf ("# seeks: %llu: bytes: %llu total_time: %f\n", cnt * nfiles, (nfiles * _read), total_time/1000000.0);
382                 printf ("%d %.4f %.4f %.4f %.5f\n", block_size, min_throughput, bandwidth, max_elapsed/1000000.0, stddev/1000000.0);
383         }
384
385         return 0;
386 }