1 #include "zcav_io.h"
2 
3 #include <unistd.h>
4 #include <sys/resource.h>
5 #include <sys/time.h>
6 #include <time.h>
7 #include <stdlib.h>
8 #include <string.h>
9 
~ZcavRead()10 ZcavRead::~ZcavRead()
11 {
12   delete m_name;
13 }
14 
Open(bool * finished,int block_size,const char * file,const char * log,int chunk_size,int do_write)15 int ZcavRead::Open(bool *finished, int block_size, const char *file
16                  , const char *log, int chunk_size, int do_write)
17 {
18   m_name = strdup(file);
19   m_finished = finished;
20   m_block_size = block_size;
21   m_chunk_size = chunk_size;
22   m_do_write = do_write;
23   m_buf = calloc(chunk_size * MEG, 1);
24 
25   if(strcmp(file, "-"))
26   {
27     if(m_do_write)
28       m_fd = file_open(file, O_WRONLY);
29     else
30       m_fd = file_open(file, O_RDONLY);
31     if(m_fd == -1)
32     {
33       fprintf(stderr, "Can't open %s\n", file);
34       return 1;
35     }
36   }
37   else
38   {
39     m_fd = 0;
40   }
41   if(strcmp(log, "-"))
42   {
43     m_logFile = true;
44     m_log = fopen(log, "w");
45     if(m_log == NULL)
46     {
47       fprintf(stderr, "Can't open %s\n", log);
48       close(m_fd);
49       return 1;
50     }
51   }
52   else
53   {
54     m_logFile = false;
55     m_log = stdout;
56   }
57   return 0;
58 }
59 
Close()60 void ZcavRead::Close()
61 {
62   if(m_logFile)
63     fclose(m_log);
64   if(m_fd != 0)
65     ::close(m_fd);
66 }
67 
writeStatus(int fd,char c)68 int ZcavRead::writeStatus(int fd, char c)
69 {
70   if(write(fd, &c, 1) != 1)
71   {
72     fprintf(stderr, "Write channel broken\n");
73     return 1;
74   }
75   return 0;
76 }
77 
Read(int max_loops,int max_size,int writeCom,int skip_rate,int start_offset)78 int ZcavRead::Read(int max_loops, int max_size, int writeCom, int skip_rate, int start_offset)
79 {
80   bool exiting = false;
81   if(max_loops == 1)
82     fprintf(m_log, "#block offset (GiB), MiB/s, time\n");
83   for(int loops = 0; !exiting && loops < max_loops; loops++)
84   {
85     int i = 0;
86     if(start_offset)
87     {
88       OFF_TYPE real_offset = OFF_TYPE(start_offset) * OFF_TYPE(m_block_size) * OFF_TYPE(1<<20);
89       if(file_lseek(m_fd, real_offset, SEEK_CUR) == OFF_TYPE(-1))
90       {
91         fprintf(stderr, "Can't lseek().\n");
92         writeStatus(writeCom, eSEEK);
93         return 1;
94       }
95       i = start_offset;
96     }
97     else
98     if(lseek(m_fd, 0, SEEK_SET))
99     {
100       fprintf(stderr, "Can't lseek().\n");
101       writeStatus(writeCom, eSEEK);
102       return 1;
103     }
104 
105     // i is block index
106     double total_read_time = 0.0;
107     bool nextLoop = false;
108     for( ; !nextLoop && (!max_size || i < max_size)
109               && (loops == 0 || (m_times[i] && m_times[i][0] != -1.0))
110               && (!max_size || i < max_size); i++)
111     {
112       double read_time = access_data(i ? skip_rate - 1 : 0);
113       if(read_time < 0.0)
114       {
115         if(i == 0)
116         {
117           fprintf(stderr, "Data file/device \"%s\" too small.\n", m_name);
118           writeStatus(writeCom, eSIZE);
119           return 1;
120         }
121         nextLoop = true;
122         break;
123       }
124       total_read_time += read_time;
125       if(max_loops == 1)
126       {
127         printavg(i * skip_rate, read_time, m_block_size);
128       }
129       else
130       {
131         if(loops == 0)
132         {
133           m_times.push_back(new double[max_loops]);
134           m_count.push_back(0);
135         }
136         m_times[i][loops] = read_time;
137         m_count[i]++;
138       }
139     } // end loop for reading blocks
140 
141     time_t now = time(NULL);
142     struct tm *cur_time = localtime(&now);
143     fprintf(stderr, "# Finished loop %d, on device %s at %d:%02d:%02d\n"
144           , loops + 1, m_name, cur_time->tm_hour, cur_time->tm_min
145           , cur_time->tm_sec);
146     fprintf(m_log, "# Read %d megs in %d seconds, %d megabytes per second.\n"
147          , i * m_block_size, int(total_read_time)
148          , int(double(i * m_block_size) / total_read_time));
149 
150     if(exiting)
151       return 1;
152   } // end loop for multiple disk reads
153   if(max_loops > 1)
154   {
155     fprintf(m_log, "#loops: %d\n", max_loops);
156     fprintf(m_log, "#block offset (GiB), MiB/s, time\n");
157     for(int i = 0; m_times[i]; i++)
158       printavg(i * skip_rate, average(m_times[i], m_count[i]), m_block_size);
159   }
160   writeStatus(writeCom, eEND);
161   return 0;
162 }
163 
printavg(int position,double avg,int block_size)164 void ZcavRead::printavg(int position, double avg, int block_size)
165 {
166   if(avg < 1.0)
167     fprintf(m_log, "#%.2f ++++ %.3f\n", float(position) * float(block_size) / 1024.0, avg);
168   else
169     fprintf(m_log, "%.2f %.2f %.3f\n", float(position) * float(block_size) / 1024.0, double(block_size) / avg, avg);
170 }
171 
compar(const void * a,const void * b)172 int compar(const void *a, const void *b)
173 {
174   double *c = (double *)(a);
175   double *d = (double *)(b);
176   if(*c < *d) return -1;
177   if(*c > *d) return 1;
178   return 0;
179 }
180 
181 // Returns the mean of the values in the array.  If the array contains
182 // more than 2 items then discard the highest and lowest thirds of the
183 // results before calculating the mean.
average(double * array,int count)184 double average(double *array, int count)
185 {
186   qsort(array, count, sizeof(double), compar);
187   int skip = count / 3;
188   int arr_items = count - (skip * 2);
189   double total = 0.0;
190   for(int i = skip; i < (count - skip); i++)
191   {
192     total += double(array[i]);
193   }
194   return total / double(arr_items);
195 }
196 
197 // just like read() or write() but will not return a partial result and the
198 // size is expressed in MEG.
access_all(int count)199 ssize_t ZcavRead::access_all(int count)
200 {
201   ssize_t total = 0;
202   count *= MEG;
203   while(total != static_cast<ssize_t>(count) )
204   {
205     ssize_t rc;
206     // for both read and write just pass the base address of the buffer
207     // as we don't care for the data, if we ever do checksums we have to
208     // change this
209     if(m_do_write)
210       rc = write(m_fd, m_buf, count - total);
211     else
212       rc = read(m_fd, m_buf, count - total);
213     if(rc == -1 || rc == 0)
214       return -1;
215     total += rc;
216   }
217   if(m_do_write && fsync(m_fd))
218     return -1;
219   return total / MEG;
220 }
221 
222 // Read/write a block of data
access_data(int skip)223 double ZcavRead::access_data(int skip)
224 {
225   if(skip)
226   {
227     OFF_TYPE real_offset = OFF_TYPE(skip) * OFF_TYPE(m_block_size) * OFF_TYPE(1<<20);
228     if(file_lseek(m_fd, real_offset, SEEK_CUR) == OFF_TYPE(-1))
229       return -1.0;
230   }
231 
232   m_dur.start();
233   for(int i = 0; i < m_block_size; i+= m_chunk_size)
234   {
235     int access_size = m_chunk_size;
236     if(i + m_chunk_size > m_block_size)
237       access_size = m_block_size - i;
238     int rc = access_all(access_size);
239     if(rc != access_size)
240       return -1.0;
241   }
242   return m_dur.stop();
243 }
244 
245