LCA2011 version
[tridge/junkcode.git] / iocache.c
1 #define _XOPEN_SOURCE 500
2 #include <unistd.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <libaio.h>
10 #include <getopt.h>
11 #include <errno.h>
12
13 #include <sys/time.h>
14 #include <time.h>
15
16 static struct timeval tp1,tp2;
17
18 static void start_timer()
19 {
20         gettimeofday(&tp1,NULL);
21 }
22
23 static double end_timer()
24 {
25         gettimeofday(&tp2,NULL);
26         return (tp2.tv_sec + (tp2.tv_usec*1.0e-6)) - 
27                 (tp1.tv_sec + (tp1.tv_usec*1.0e-6));
28 }
29
30
31 struct io_block {
32         struct iocb *cb;
33         unsigned char *buffer;
34         int nread;
35         int done;
36         int blk;
37 };
38
39 struct io_ring {
40         int fd;
41         unsigned block_size;
42         unsigned num_blocks;
43         struct io_block *blocks;
44         io_context_t io_ctx;
45 };
46
47 static int a_schedule(struct io_ring *ring, int blk)
48 {
49         struct io_block *b = &ring->blocks[blk % ring->num_blocks];
50         
51         if (blk == b->blk) return 0;
52
53         if (b->buffer == NULL) {
54                 b->buffer = malloc(ring->block_size);
55                 if (b->buffer == NULL) goto failed;
56         }
57
58         if (b->cb == NULL) {
59                 b->cb = malloc(sizeof(*b->cb));
60                 if (b->cb == NULL) goto failed;
61         }
62
63         if (b->blk != -1 && !b->done) {
64                 struct io_event res;
65                 int ret = io_cancel(ring->io_ctx, b->cb, &res);
66                 if (ret == EAGAIN) {
67                         goto failed;
68                 }
69         }
70
71         b->blk = blk;
72         b->done = 0;
73         memset(b->cb, 0, sizeof(*b->cb));
74         io_prep_pread(b->cb, ring->fd, b->buffer, ring->block_size,
75                       blk * (off_t)ring->block_size);
76         if (io_submit(ring->io_ctx, 1, &b->cb) != 0) goto failed;
77         b->cb->key = blk;
78         return 0;
79
80 failed:
81         free(b->buffer);
82         free(b->cb);
83         b->buffer = NULL;
84         b->cb     = NULL;
85         b->blk    = -1;
86         return -1;
87 }
88
89 static int a_wait(struct io_ring *ring, int blk)
90 {
91         struct io_block *b = &ring->blocks[blk % ring->num_blocks];
92         struct io_event ev[10];
93
94         if (b->blk != blk) return -1;
95
96         while (!b->done) {
97                 int i, nr = io_getevents(ring->io_ctx, 1, 10, ev, NULL);
98                 for (i=0;i<nr;i++) {
99                         struct iocb *cb = ev[i].obj;
100                         struct io_block *b2 = &ring->blocks[cb->key % ring->num_blocks];
101                         b2->done = 1;
102                         b2->nread = ev[i].res;
103                 }
104         }
105
106         if (b->nread < 0) return -1;
107
108         return 0;
109 }
110
111
112 static ssize_t a_read(struct io_ring *ring, void *buf, size_t count, off_t offset)
113 {
114         int blk_start = offset / ring->block_size;
115         int blk_end   = (offset+count-1) / ring->block_size;
116         int blk_sched = blk_start + (ring->num_blocks/2);
117         int i;
118         ssize_t total=0;
119
120         if (blk_sched < blk_end) blk_sched = blk_end;
121
122         for (i=blk_start;i<=blk_sched;i++) {
123                 a_schedule(ring, i);
124         }
125
126         while (count) {
127                 int n = count;
128                 int blk_offset = offset % ring->block_size;
129                 int blk = offset / ring->block_size;
130                 struct io_block *b = &ring->blocks[blk % ring->num_blocks];
131                 ssize_t nread;
132                 if (n > ring->block_size - blk_offset) {
133                         n = ring->block_size - blk_offset;
134                 }
135                 if (a_wait(ring, blk) != 0) {
136                         nread = pread(ring->fd, buf, n, offset);
137                         printf("sync fallback\n");
138                 } else {
139                         if (n > b->nread) n = b->nread;
140                         memcpy(buf, b->buffer + blk_offset, n);
141                         nread = n;
142                 }
143                 if (nread <= 0) {
144                         if (total > 0) return total;
145                         return nread;
146                 }
147                 total += nread;
148                 buf = (void *)(nread + (char *)buf);
149                 count -= nread;
150                 offset += nread;
151         }
152
153         return total;
154 }
155
156 static struct io_ring *a_init(int fd, int block_size, int nblocks)
157 {
158         struct io_ring *ring;
159         int i;
160         int ret;
161
162         ring = malloc(sizeof(*ring));
163         if (ring == NULL) return NULL;
164
165         memset(ring, 0, sizeof(*ring));
166         ret = io_queue_init(nblocks, &ring->io_ctx);
167         if (ret != 0) {
168                 free(ring);
169                 return NULL;
170         }
171         ring->fd = fd;
172         ring->num_blocks = nblocks;
173         ring->block_size = block_size;
174         ring->blocks = calloc(nblocks, sizeof(ring->blocks[0]));
175         if (ring->blocks == NULL) {
176                 free(ring);
177                 return NULL;
178         }
179
180         for (i=0;i<nblocks;i++) {
181                 ring->blocks[i].blk = -1;
182         }
183         return ring;
184 }
185
186 static void a_close(struct io_ring *ring)
187 {
188         int i;
189
190         for (i=0;i<ring->num_blocks;i++) {
191                 struct io_block *b = &ring->blocks[i];
192                 if (b->blk != -1 && !b->done) {
193                         struct io_event res;
194                         int ret = io_cancel(ring->io_ctx, b->cb, &res);
195                         if (ret == EAGAIN) {
196                                 a_wait(ring, b->blk);
197                         }
198                 }
199                 if (b->cb) free(b->cb);
200                 if (b->buffer) free(b->buffer);
201         }
202         free(ring);
203 }
204
205 static void async_test(const char *fname, int block_size, int nblocks, int rsize)
206 {
207         int fd;
208         struct io_ring *ring;
209         off_t offset=0;
210         char buf[rsize];
211         int i=0;
212
213         offset=0;
214         start_timer();
215         fd = open(fname, O_RDONLY);
216         if (fd == -1) {
217                 perror(fname);
218                 exit(1);
219         }
220
221         ring = a_init(fd, block_size, nblocks);
222         if (ring == NULL) {
223                 fprintf(stderr, "a_init faild\n");
224                 exit(1);
225         }
226
227         while (a_read(ring, buf, sizeof(buf), offset) > 0) {
228                 offset += sizeof(buf);
229                 if (++i % 5 == 0) {
230                         offset -= 2*sizeof(buf);
231                 }
232         }
233
234         printf("async: %.3f MByte/sec\n", 1.0e-6 * offset / end_timer());
235         a_close(ring);
236 }
237
238 static void sync_test(const char *fname, int rsize)
239 {
240         int fd;
241         off_t offset=0;
242         char buf[rsize];
243         int i=0;
244
245         offset=0;
246         start_timer();
247         fd = open(fname, O_RDONLY);
248         if (fd == -1) {
249                 perror(fname);
250                 exit(1);
251         }
252
253         while (pread(fd, buf, sizeof(buf), offset) > 0) {
254                 offset += sizeof(buf);
255                 if (++i % 5 == 0) {
256                         offset -= 2*sizeof(buf);
257                 }
258         }
259
260         printf("sync : %.3f MByte/sec\n", 1.0e-6 * offset / end_timer());
261         close(fd);
262 }
263
264 static void usage(void)
265 {
266         printf("Usage: aiocache [options] <filename>\n");
267         printf(
268 "Options:\n" \
269 " -b block_size\n" \
270 " -n nblocks\n" \
271 " -r readsize\n");
272 }
273
274 int main(int argc, const char *argv[])
275 {
276         const char *fname;
277         int opt;
278         int block_size = 1024*1024;
279         int nblocks = 100;
280         int rsize = 32*1024;
281
282         while ((opt = getopt(argc, argv, "b:n:r:")) != -1) {
283                 switch (opt) {
284                 case 'b':
285                         block_size = strtol(optarg, NULL, 0);
286                         break;
287                 case 'n':
288                         nblocks = strtol(optarg, NULL, 0);
289                         break;
290                 case 'r':
291                         rsize = strtol(optarg, NULL, 0);
292                         break;
293                 default:
294                         printf("Invalid option '%c'\n", opt);
295                         usage();
296                         exit(1);
297                 }
298         }
299
300         argv += optind;
301         argc -= optind;
302
303         if (argc < 1) {
304                 usage();
305                 exit(1);
306         }
307
308         fname = argv[0];
309
310         printf("Testing with block_size=%d nblocks=%d rsize=%d\n",
311                block_size,nblocks,rsize);
312         
313         async_test(fname, block_size, nblocks, rsize);
314         sync_test(fname, rsize);
315         async_test(fname, block_size, nblocks, rsize);
316         sync_test(fname, rsize);
317
318         return 0;       
319 }