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