iobw.c revision 60aa78315174e5bf9d98f66db582ad381e013817
1/* 2 * iobw.c - simple I/O bandwidth benchmark 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public 15 * License along with this program; if not, write to the 16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 * Boston, MA 021110-1307, USA. 18 * 19 * Copyright (C) 2008 Andrea Righi <righi.andrea@gmail.com> 20 */ 21 22#define _GNU_SOURCE 23#define __USE_GNU 24 25#include <errno.h> 26#include <stdio.h> 27#include <stdlib.h> 28#include <malloc.h> 29#include <fcntl.h> 30#include <signal.h> 31#include <string.h> 32#include <unistd.h> 33#include <sys/types.h> 34#include <sys/stat.h> 35#include <sys/time.h> 36#include <sys/wait.h> 37#include <asm/page.h> 38 39#ifndef PAGE_SIZE 40#define PAGE_SIZE getpagesize() 41#endif 42 43#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) 44#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) 45#define KB(x) ((x) >> 10) 46 47const char usage[] = "Usage: iobw [-direct] threads chunk_size data_size\n"; 48const char child_fmt[] = 49 "(%s) task %3d: time %4lu.%03lu bw %7lu KiB/s (%s)\n"; 50const char parent_fmt[] = 51 "(%s) parent %d: time %4lu.%03lu bw %7lu KiB/s (%s)\n"; 52 53static int directio = 0; 54static size_t data_size = 0; 55static size_t chunk_size = 0; 56 57typedef enum { 58 OP_WRITE, 59 OP_READ, 60 NUM_IOPS, 61} iops_t; 62 63static const char *iops[] = { 64 "WRITE", 65 "READ ", 66 "TOTAL", 67}; 68 69static int threads; 70pid_t *children; 71 72char *mygroup; 73 74static void print_results(int id, iops_t op, size_t bytes, struct timeval *diff) 75{ 76 fprintf(stdout, id ? child_fmt : parent_fmt, 77 mygroup, id, diff->tv_sec, diff->tv_usec / 1000, 78 (bytes / (diff->tv_sec * 1000000L + diff->tv_usec)) 79 * 1000000L / 1024, iops[op]); 80} 81 82static void thread(int id) 83{ 84 struct timeval start, stop, diff; 85 int fd, i, ret; 86 size_t n; 87 void *buf; 88 int flags = O_CREAT | O_RDWR | O_LARGEFILE; 89 char filename[32]; 90 91 ret = posix_memalign(&buf, PAGE_SIZE, chunk_size); 92 if (ret < 0) { 93 fprintf(stderr, 94 "ERROR: task %d couldn't allocate %lu bytes (%s)\n", 95 id, chunk_size, strerror(errno)); 96 exit(1); 97 } 98 memset(buf, 0xaa, chunk_size); 99 100 snprintf(filename, sizeof(filename), "%s-%d-iobw.tmp", mygroup, id); 101 if (directio) 102 flags |= O_DIRECT; 103 fd = open(filename, flags, 0600); 104 if (fd < 0) { 105 fprintf(stderr, "ERROR: task %d couldn't open %s (%s)\n", 106 id, filename, strerror(errno)); 107 free(buf); 108 exit(1); 109 } 110 111 /* Write */ 112 lseek(fd, 0, SEEK_SET); 113 n = 0; 114 gettimeofday(&start, NULL); 115 while (n < data_size) { 116 i = write(fd, buf, chunk_size); 117 if (i < 0) { 118 fprintf(stderr, "ERROR: task %d writing to %s (%s)\n", 119 id, filename, strerror(errno)); 120 ret = 1; 121 goto out; 122 } 123 n += i; 124 } 125 gettimeofday(&stop, NULL); 126 timersub(&stop, &start, &diff); 127 print_results(id + 1, OP_WRITE, data_size, &diff); 128 129 /* Read */ 130 lseek(fd, 0, SEEK_SET); 131 n = 0; 132 gettimeofday(&start, NULL); 133 while (n < data_size) { 134 i = read(fd, buf, chunk_size); 135 if (i < 0) { 136 fprintf(stderr, "ERROR: task %d reading to %s (%s)\n", 137 id, filename, strerror(errno)); 138 ret = 1; 139 goto out; 140 } 141 n += i; 142 } 143 gettimeofday(&stop, NULL); 144 timersub(&stop, &start, &diff); 145 print_results(id + 1, OP_READ, data_size, &diff); 146out: 147 close(fd); 148 unlink(filename); 149 free(buf); 150 exit(ret); 151} 152 153static void spawn(int id) 154{ 155 pid_t pid; 156 157 pid = fork(); 158 switch (pid) { 159 case -1: 160 fprintf(stderr, "ERROR: couldn't fork thread %d\n", id); 161 exit(1); 162 case 0: 163 thread(id); 164 default: 165 children[id] = pid; 166 } 167} 168 169void signal_handler(int sig) 170{ 171 char filename[32]; 172 int i; 173 174 for (i = 0; i < threads; i++) 175 if (children[i]) 176 kill(children[i], SIGKILL); 177 178 for (i = 0; i < threads; i++) { 179 struct stat mystat; 180 181 snprintf(filename, sizeof(filename), "%s-%d-iobw.tmp", 182 mygroup,i); 183 if (stat(filename, &mystat) < 0) 184 continue; 185 unlink(filename); 186 } 187 188 fprintf(stdout, "received signal %d, exiting\n", sig); 189 exit(0); 190} 191 192unsigned long long memparse(char *ptr, char **retptr) 193{ 194 unsigned long long ret = strtoull(ptr, retptr, 0); 195 196 switch (**retptr) { 197 case 'G': 198 case 'g': 199 ret <<= 10; 200 case 'M': 201 case 'm': 202 ret <<= 10; 203 case 'K': 204 case 'k': 205 ret <<= 10; 206 (*retptr)++; 207 default: 208 break; 209 } 210 return ret; 211} 212 213int main(int argc, char *argv[]) 214{ 215 struct timeval start, stop, diff; 216 char *end; 217 int i; 218 219 if (argv[1] && strcmp(argv[1], "-direct") == 0) { 220 directio = 1; 221 argc--; 222 argv++; 223 } 224 if (argc != 4) { 225 fprintf(stderr, usage); 226 exit(1); 227 } 228 if ((threads = atoi(argv[1])) == 0) { 229 fprintf(stderr, usage); 230 exit(1); 231 } 232 chunk_size = ALIGN(memparse(argv[2], &end), PAGE_SIZE); 233 if (*end) { 234 fprintf(stderr, usage); 235 exit(1); 236 } 237 data_size = ALIGN(memparse(argv[3], &end), PAGE_SIZE); 238 if (*end) { 239 fprintf(stderr, usage); 240 exit(1); 241 } 242 243 /* retrieve group name */ 244 mygroup = getenv("MYGROUP"); 245 if (!mygroup) { 246 fprintf(stderr, 247 "ERROR: undefined environment variable MYGROUP\n"); 248 exit(1); 249 } 250 251 children = malloc(sizeof(pid_t) * threads); 252 if (!children) { 253 fprintf(stderr, "ERROR: not enough memory\n"); 254 exit(1); 255 } 256 257 /* handle user interrupt */ 258 signal(SIGINT, signal_handler); 259 /* handle kill from shell */ 260 signal(SIGTERM, signal_handler); 261 262 fprintf(stdout, "chunk_size %luKiB, data_size %luKiB\n", 263 KB(chunk_size), KB(data_size)); 264 fflush(stdout); 265 266 gettimeofday(&start, NULL); 267 for (i = 0; i < threads ; i++) 268 spawn(i); 269 for (i = 0; i < threads; i++) { 270 int status; 271 wait(&status); 272 if (!WIFEXITED(status)) 273 exit(1); 274 } 275 gettimeofday(&stop, NULL); 276 277 timersub(&stop, &start, &diff); 278 print_results(0, NUM_IOPS, data_size * threads * NUM_IOPS, &diff); 279 fflush(stdout); 280 free(children); 281 282 exit(0); 283} 284