new read tester for investigating read bandwidth issues
[ardour.git] / tools / sfrtest.cc
1 /* g++ -o sfrtest sfrtest.cc `pkg-config --cflags --libs sndfile` `pkg-config --cflags --libs glibmm-2.4` */
2
3 #include <vector>
4 #include <iostream>
5 #include <iomanip>
6 #include <sstream>
7 #include <cstdlib>
8 #include <cerrno>
9
10 #include <unistd.h>
11 #include <sndfile.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <getopt.h>
15 #include <fcntl.h>
16
17 #include <glibmm/miscutils.h>
18 #include <glibmm/fileutils.h>
19
20 using namespace std;
21
22 SF_INFO format_info;
23 float* data = 0;
24 bool with_sync = false;
25
26 int
27 read_one (SNDFILE* sf, uint32_t nframes)
28 {
29         if (sf_read_float (sf, (float*) data, nframes) != nframes) {
30                 return -1;
31         }
32
33         if (with_sync) {
34                 sf_write_sync (sf);
35         }
36
37         return 0;
38 }
39
40 void
41 usage ()
42 {
43         cout << "sfrtest [ -n NFILES ] [ -b BLOCKSIZE ] [ -s ] [ -D ] filename-template" << endl;
44 }
45
46 int
47 main (int argc, char* argv[])
48 {
49         vector<SNDFILE*> sndfiles;
50         uint32_t sample_size;
51         char optstring[] = "n:b:s";
52         uint32_t block_size = 64 * 1024;
53         uint32_t nfiles = 100;
54         bool direct = false;
55         const struct option longopts[] = {
56                 { "nfiles", 1, 0, 'n' },
57                 { "blocksize", 1, 0, 'b' },
58                 { "sync", 0, 0, 's' },
59                 { "direct", 0, 0, 'D' },
60                 { 0, 0, 0, 0 }
61         };
62
63         int option_index = 0;
64         int c = 0;
65         char const * name_template = 0;
66         int samplerate;
67         
68         while (1) {
69                 if ((c = getopt_long (argc, argv, optstring, longopts, &option_index)) == -1) {
70                         break;
71                 }
72
73                 switch (c) {
74                 case 'n':
75                         nfiles = atoi (optarg);
76                         break;
77                 case 'b':
78                         block_size = atoi (optarg);
79                         break;
80                 case 's':
81                         with_sync = true;
82                         break;
83                 case 'D':
84                         direct = true;
85                         break;
86                 default:
87                         usage ();
88                         return 0;
89                 }
90         }
91
92         if (optind < argc) {
93                 name_template = argv[optind];
94         } else {
95                 usage ();
96                 return 1;
97         }
98         
99         for (uint32_t n = 1; n <= nfiles; ++n) {
100                 SNDFILE* sf;
101                 char path[PATH_MAX+1];
102
103                 snprintf (path, sizeof (path), name_template, n);
104
105                 if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
106                         break;
107                 }
108                 
109                 int flags = O_RDONLY;
110                 int fd = open (path, flags, 0644);
111
112                 if (fd < 0) {
113                         cerr << "Could not open file #" << n << " @ " << path << " (" << strerror (errno) << ")" << endl;
114                         return 1;
115                 }
116
117 #ifdef __APPLE__
118                 if (direct) {
119                         /* Apple man pages say only that it returns "a value other than -1 on success",
120                            which probably means zero, but you just can't be too careful with
121                            those guys.
122                         */
123                         if (fcntl (fd, F_NOCACHE, 1) == -1) {
124                                 cerr << "Cannot set F_NOCACHE on file # " << n << endl;
125                         }
126                 }
127 #endif
128
129                 if ((sf = sf_open_fd (fd, SFM_READ, &format_info, true)) == 0) {
130                         cerr << "Could not open SNDFILE #" << n << " @ " << path << " (" << sf_strerror (0) << ")" << endl;
131                         return 1;
132                 }
133
134                 samplerate = format_info.samplerate;
135                 
136                 sndfiles.push_back (sf);
137         }
138
139         cout << "Discovered " << nfiles+1 << " files using " << name_template << endl;
140                 
141         data = new float[block_size];
142         uint64_t read = 0;
143         
144         while (true) {
145                 gint64 before;
146                 before = g_get_monotonic_time();
147                 for (vector<SNDFILE*>::iterator s = sndfiles.begin(); s != sndfiles.end(); ++s) {
148                         if (read_one (*s, block_size)) {
149                                 cerr << "Read failed for file #" << distance (sndfiles.begin(), s) << endl;
150                                 return 1;
151                         }
152                 }
153                 read += block_size;
154                 gint64 elapsed = g_get_monotonic_time() - before;
155                 double bandwidth = (sndfiles.size() * block_size * sample_size) / (elapsed/1000000.0);
156                 double data_minutes = read / (double) (60.0 * 48000.0);
157                 const double data_rate = sndfiles.size() * sample_size * samplerate;
158                 stringstream ds;
159                 ds << setprecision (1) << data_minutes;
160                 
161                 cout << "BW @ " << read << " frames (" << ds.str() << " minutes) = " << (bandwidth/1048576.0) <<  " MB/sec " << bandwidth / data_rate << " x faster than necessary " << endl;
162         }
163
164         return 0;
165 }
166
167