1 /* 2 * Raster benchmark program for CUPS. 3 * 4 * Copyright 2007-2016 by Apple Inc. 5 * Copyright 1997-2006 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #include <config.h> 21 #include <cups/raster.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <sys/time.h> 25 #include <signal.h> 26 #include <unistd.h> 27 #include <sys/wait.h> 28 29 30 /* 31 * Constants... 32 */ 33 34 #define TEST_WIDTH 1024 35 #define TEST_HEIGHT 1024 36 #define TEST_PAGES 16 37 #define TEST_PASSES 20 38 39 40 /* 41 * Local functions... 42 */ 43 44 static double compute_median(double *secs); 45 static double get_time(void); 46 static void read_test(int fd); 47 static int run_read_test(void); 48 static void write_test(int fd, cups_mode_t mode); 49 50 51 /* 52 * 'main()' - Benchmark the raster read/write functions. 53 */ 54 55 int /* O - Exit status */ 56 main(int argc, /* I - Number of command-line args */ 57 char *argv[]) /* I - Command-line arguments */ 58 { 59 int i; /* Looping var */ 60 int ras_fd, /* File descriptor for read process */ 61 status; /* Exit status of read process */ 62 double start_secs, /* Start time */ 63 write_secs, /* Write time */ 64 read_secs, /* Read time */ 65 pass_secs[TEST_PASSES]; /* Total test times */ 66 cups_mode_t mode; /* Write mode */ 67 68 69 /* 70 * See if we have anything on the command-line... 71 */ 72 73 if (argc > 2 || (argc == 2 && strcmp(argv[1], "-z"))) 74 { 75 puts("Usage: rasterbench [-z]"); 76 return (1); 77 } 78 79 mode = argc > 1 ? CUPS_RASTER_WRITE_COMPRESSED : CUPS_RASTER_WRITE; 80 81 /* 82 * Ignore SIGPIPE... 83 */ 84 85 signal(SIGPIPE, SIG_IGN); 86 87 /* 88 * Run the tests several times to get a good average... 89 */ 90 91 printf("Test read/write speed of %d pages, %dx%d pixels...\n\n", 92 TEST_PAGES, TEST_WIDTH, TEST_HEIGHT); 93 for (i = 0; i < TEST_PASSES; i ++) 94 { 95 printf("PASS %2d: ", i + 1); 96 fflush(stdout); 97 98 ras_fd = run_read_test(); 99 start_secs = get_time(); 100 101 write_test(ras_fd, mode); 102 103 write_secs = get_time(); 104 printf(" %.3f write,", write_secs - start_secs); 105 fflush(stdout); 106 107 close(ras_fd); 108 wait(&status); 109 110 read_secs = get_time(); 111 pass_secs[i] = read_secs - start_secs; 112 printf(" %.3f read, %.3f total\n", read_secs - write_secs, pass_secs[i]); 113 } 114 115 printf("\nMedian Total Time: %.3f seconds per document\n", 116 compute_median(pass_secs)); 117 118 return (0); 119 } 120 121 122 /* 123 * 'compute_median()' - Compute the median time for a test. 124 */ 125 126 static double /* O - Median time in seconds */ 127 compute_median(double *secs) /* I - Array of time samples */ 128 { 129 int i, j; /* Looping vars */ 130 double temp; /* Swap variable */ 131 132 133 /* 134 * Sort the array into ascending order using a quicky bubble sort... 135 */ 136 137 for (i = 0; i < (TEST_PASSES - 1); i ++) 138 for (j = i + 1; j < TEST_PASSES; j ++) 139 if (secs[i] > secs[j]) 140 { 141 temp = secs[i]; 142 secs[i] = secs[j]; 143 secs[j] = temp; 144 } 145 146 /* 147 * Return the average of the middle two samples... 148 */ 149 150 return (0.5 * (secs[TEST_PASSES / 2 - 1] + secs[TEST_PASSES / 2])); 151 } 152 153 154 /* 155 * 'get_time()' - Get the current time in seconds. 156 */ 157 158 static double /* O - Time in seconds */ 159 get_time(void) 160 { 161 struct timeval curtime; /* Current time */ 162 163 164 gettimeofday(&curtime, NULL); 165 return (curtime.tv_sec + 0.000001 * curtime.tv_usec); 166 } 167 168 169 /* 170 * 'read_test()' - Benchmark the raster read functions. 171 */ 172 173 static void 174 read_test(int fd) /* I - File descriptor to read from */ 175 { 176 unsigned y; /* Looping var */ 177 cups_raster_t *r; /* Raster stream */ 178 cups_page_header2_t header; /* Page header */ 179 unsigned char buffer[8 * TEST_WIDTH]; 180 /* Read buffer */ 181 182 183 /* 184 * Test read speed... 185 */ 186 187 if ((r = cupsRasterOpen(fd, CUPS_RASTER_READ)) == NULL) 188 { 189 perror("Unable to create raster input stream"); 190 return; 191 } 192 193 while (cupsRasterReadHeader2(r, &header)) 194 { 195 for (y = 0; y < header.cupsHeight; y ++) 196 cupsRasterReadPixels(r, buffer, header.cupsBytesPerLine); 197 } 198 199 cupsRasterClose(r); 200 } 201 202 203 /* 204 * 'run_read_test()' - Run the read test as a child process via pipes. 205 */ 206 207 static int /* O - Standard input of child */ 208 run_read_test(void) 209 { 210 int ras_pipes[2]; /* Raster data pipes */ 211 int pid; /* Child process ID */ 212 213 214 if (pipe(ras_pipes)) 215 return (-1); 216 217 if ((pid = fork()) < 0) 218 { 219 /* 220 * Fork error - return -1 on error... 221 */ 222 223 close(ras_pipes[0]); 224 close(ras_pipes[1]); 225 226 return (-1); 227 } 228 else if (pid == 0) 229 { 230 /* 231 * Child comes here - read data from the input pipe... 232 */ 233 234 close(ras_pipes[1]); 235 read_test(ras_pipes[0]); 236 exit(0); 237 } 238 else 239 { 240 /* 241 * Parent comes here - return the output pipe... 242 */ 243 244 close(ras_pipes[0]); 245 return (ras_pipes[1]); 246 } 247 } 248 249 250 /* 251 * 'write_test()' - Benchmark the raster write functions. 252 */ 253 254 static void 255 write_test(int fd, /* I - File descriptor to write to */ 256 cups_mode_t mode) /* I - Write mode */ 257 { 258 unsigned page, x, y; /* Looping vars */ 259 unsigned count; /* Number of bytes to set */ 260 cups_raster_t *r; /* Raster stream */ 261 cups_page_header2_t header; /* Page header */ 262 unsigned char data[32][8 * TEST_WIDTH]; 263 /* Raster data to write */ 264 265 266 /* 267 * Create a combination of random data and repeated data to simulate 268 * text with some whitespace. 269 */ 270 271 CUPS_SRAND(time(NULL)); 272 273 memset(data, 0, sizeof(data)); 274 275 for (y = 0; y < 28; y ++) 276 { 277 for (x = CUPS_RAND() & 127, count = (CUPS_RAND() & 15) + 1; 278 x < sizeof(data[0]); 279 x ++, count --) 280 { 281 if (count <= 0) 282 { 283 x += (CUPS_RAND() & 15) + 1; 284 count = (CUPS_RAND() & 15) + 1; 285 286 if (x >= sizeof(data[0])) 287 break; 288 } 289 290 data[y][x] = (unsigned char)CUPS_RAND(); 291 } 292 } 293 294 /* 295 * Test write speed... 296 */ 297 298 if ((r = cupsRasterOpen(fd, mode)) == NULL) 299 { 300 perror("Unable to create raster output stream"); 301 return; 302 } 303 304 for (page = 0; page < TEST_PAGES; page ++) 305 { 306 memset(&header, 0, sizeof(header)); 307 header.cupsWidth = TEST_WIDTH; 308 header.cupsHeight = TEST_HEIGHT; 309 header.cupsBytesPerLine = TEST_WIDTH; 310 311 if (page & 1) 312 { 313 header.cupsBytesPerLine *= 4; 314 header.cupsColorSpace = CUPS_CSPACE_CMYK; 315 header.cupsColorOrder = CUPS_ORDER_CHUNKED; 316 } 317 else 318 { 319 header.cupsColorSpace = CUPS_CSPACE_K; 320 header.cupsColorOrder = CUPS_ORDER_BANDED; 321 } 322 323 if (page & 2) 324 { 325 header.cupsBytesPerLine *= 2; 326 header.cupsBitsPerColor = 16; 327 header.cupsBitsPerPixel = (page & 1) ? 64 : 16; 328 } 329 else 330 { 331 header.cupsBitsPerColor = 8; 332 header.cupsBitsPerPixel = (page & 1) ? 32 : 8; 333 } 334 335 cupsRasterWriteHeader2(r, &header); 336 337 for (y = 0; y < TEST_HEIGHT; y ++) 338 cupsRasterWritePixels(r, data[y & 31], header.cupsBytesPerLine); 339 } 340 341 cupsRasterClose(r); 342 } 343