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