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 */
main(int argc,char * argv[])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 */
compute_median(double * secs)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 */
get_time(void)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
read_test(int fd)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 */
run_read_test(void)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
write_test(int fd,cups_mode_t mode)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