a222ffffc6189626e6c5cad70a4a0eef05d40f34
[xfstests-dev.git] / src / dirperf.c
1 /*
2  * Copyright (c) 2000-2004 Silicon Graphics, Inc.
3  * All Rights Reserved.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it would be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write the Free Software Foundation,
16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include <sys/param.h>
20 #include <sys/stat.h>
21 #include <sys/time.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <math.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #define MAXNAMELEN 256
32 #ifndef __sgi__
33 typedef unsigned int uint_t;
34 #endif
35
36 /*
37  * Loop over directory sizes:
38  *      make m directories
39  *      touch n files in each directory
40  *      stat the files round-robin
41  *      readdir/unlink the files 
42  * Change directory sizes by multiplication or addition.
43  * Allow control of starting & stopping sizes, name length, target directory.
44  * Print size and wallclock time (ms per file).
45  * Output can be used to make graphs (gnuplot)
46  */
47
48 static uint_t   addval;
49 static uint_t   dirchars;
50 static char     *directory;
51 static uint_t   firstsize;
52 static uint_t   lastsize;
53 static uint_t   minchars;
54 static double   mulval;
55 static uint_t   nchars;
56 static uint_t   ndirs;
57 static uint_t   pfxchars;
58 static uint_t   stats;
59
60 static void     filename(int, int, char *);
61 static int      hexchars(uint_t);
62 static uint_t   nextsize(uint_t);
63 static double   now(void);
64 static void     usage(void);
65
66 /*
67  * Maximum size allowed, this is pretty nuts.
68  * The largest one we've ever built has been about 2 million.
69  */
70 #define MAX_DIR_SIZE    (16 * 1024 * 1024)
71 #define DFL_FIRST_SIZE  1
72 #define DFL_LAST_SIZE   (1024 * 1024)
73 #define MAX_DIR_COUNT   1024
74 #define MIN_DIR_COUNT   1
75
76 int
77 main(int argc, char **argv)
78 {
79         int             c;
80         uint_t          cursize;
81         DIR             *dirp;
82         int             i;
83         int             j;
84         char            name[MAXNAMELEN];
85         struct stat     stb;
86         double          stime;
87
88         while ((c = getopt(argc, argv, "a:c:d:f:l:m:n:s:")) != -1) {
89                 switch (c) {
90                 case 'a':
91                         addval = (uint_t)atoi(optarg);
92                         break;
93                 case 'c':
94                         nchars = (uint_t)atoi(optarg);
95                         break;
96                 case 'd':
97                         directory = optarg;
98                         break;
99                 case 'f':
100                         firstsize = (uint_t)atoi(optarg);
101                         break;
102                 case 'l':
103                         lastsize = (uint_t)atoi(optarg);
104                         break;
105                 case 'm':
106                         mulval = atof(optarg);
107                         break;
108                 case 'n':
109                         ndirs = (uint_t)atoi(optarg);
110                         break;
111                 case 's':
112                         stats = (uint_t)atoi(optarg);
113                         break;
114                 case '?':
115                 default:
116                         usage();
117                         exit(1);
118                 }
119         }
120         if (!addval && !mulval)
121                 mulval = 2.0;
122         else if ((addval && mulval) || mulval < 0.0) {
123                 usage();
124                 exit(1);
125         }
126         if (stats == 0)
127                 stats = 1;
128         if (!directory)
129                 directory = ".";
130         else {
131                 if (mkdir(directory, 0777) < 0 && errno != EEXIST) {
132                         perror(directory);
133                         exit(1);
134                 }
135                 if (chdir(directory) < 0) {
136                         perror(directory);
137                         exit(1);
138                 }
139         }
140         if (firstsize == 0)
141                 firstsize = DFL_FIRST_SIZE;
142         else if (firstsize > MAX_DIR_SIZE)
143                 firstsize = MAX_DIR_SIZE;
144         if (lastsize == 0)
145                 lastsize = DFL_LAST_SIZE;
146         else if (lastsize > MAX_DIR_SIZE)
147                 lastsize = MAX_DIR_SIZE;
148         if (lastsize < firstsize)
149                 lastsize = firstsize;
150         minchars = hexchars(lastsize - 1);
151         if (nchars < minchars)
152                 nchars = minchars;
153         else if (nchars >= MAXNAMELEN)
154                 nchars = MAXNAMELEN - 1;
155         if (ndirs > MAX_DIR_COUNT)
156                 ndirs = MAX_DIR_COUNT;
157         if (ndirs < MIN_DIR_COUNT)
158                 ndirs = MIN_DIR_COUNT;
159         dirchars = hexchars(ndirs);
160         pfxchars = nchars - minchars;
161         if (pfxchars)
162                 memset(&name[dirchars + 1], 'a', pfxchars);
163         for (j = 0; j < ndirs; j++) {
164                 filename(0, j, name);
165                 name[dirchars] = '\0';
166                 mkdir(name, 0777);
167         }
168         for (cursize = firstsize;
169              cursize <= lastsize;
170              cursize = nextsize(cursize)) {
171                 stime = now();
172                 for (i = 0; i < cursize; i++) {
173                         for (j = 0; j < ndirs; j++) {
174                                 filename((i + j) % cursize, j, name);
175                                 close(creat(name, 0666));
176                         }
177                 }
178                 for (i = 0; i < cursize * stats; i++) {
179                         for (j = 0; j < ndirs; j++) {
180                                 filename((i + j) % cursize, j, name);
181                                 stat(name, &stb);
182                         }
183                 }
184                 for (j = 0; j < ndirs; j++) {
185                         filename(0, j, name);
186                         name[dirchars] = '\0';
187                         dirp = opendir(name);
188                         while (readdir(dirp))
189                                 continue;
190                         closedir(dirp);
191                 }
192                 for (i = 0; i < cursize; i++) {
193                         for (j = 0; j < ndirs; j++) {
194                                 filename((i + j) % cursize, j, name);
195                                 unlink(name);
196                         }
197                 }
198                 printf("%d %.3f\n", cursize,
199                         (now() - stime) * 1.0e3 / (cursize * ndirs));
200         }
201         for (j = 0; j < ndirs; j++) {
202                 filename(0, j, name);
203                 name[dirchars] = '\0';
204                 rmdir(name);
205         }
206         return 0;
207 }
208
209 static void
210 filename(int idx, int dir, char *name)
211 {
212         static char     hexc[16] = "0123456789abcdef";
213         int             i;
214
215         for (i = dirchars - 1; i >= 0; i--)
216                 *name++ = hexc[(dir >> (4 * i)) & 0xf];
217         *name++ = '/';
218         name += pfxchars;               /* skip pfx a's */
219         for (i = minchars - 1; i >= 0; i--)
220                 *name++ = hexc[(idx >> (4 * i)) & 0xf];
221         *name = '\0';
222 }
223
224 static int
225 hexchars(uint_t maxval)
226 {
227         if (maxval < 16)
228                 return 1;
229         if (maxval < 16 * 16)
230                 return 2;
231         if (maxval < 16 * 16 * 16)
232                 return 3;
233         if (maxval < 16 * 16 * 16 * 16)
234                 return 4;
235         if (maxval < 16 * 16 * 16 * 16 * 16)
236                 return 5;
237         if (maxval < 16 * 16 * 16 * 16 * 16 * 16)
238                 return 6;
239         if (maxval < 16 * 16 * 16 * 16 * 16 * 16 * 16)
240                 return 7;
241         return 8;
242 }
243
244 static uint_t
245 nextsize(uint_t cursize)
246 {
247         double  n;
248
249         n = cursize;
250         if (addval)
251                 n += addval;
252         else
253                 n *= mulval;
254         if (n > (double)lastsize + 0.5)
255                 return lastsize + 1;    /* i.e. out of bounds */
256         else if ((uint_t)n == cursize)
257                 return cursize + 1;
258         else
259                 return (uint_t)n;
260 }
261
262 static double
263 now(void)
264 {
265         struct timeval  tv;
266
267         gettimeofday(&tv, NULL);
268         return (double)tv.tv_sec + 1.0e-6 * (double)tv.tv_usec;
269 }
270
271 static void
272 usage(void)
273 {
274         fprintf(stderr,
275                 "usage: dirperf [-d dir] [-a addstep | -m mulstep] [-f first] "
276                 "[-l last] [-c nchars] [-n ndirs] [-s nstats]\n");
277 }