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