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