• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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