Summary
This article is for developers interested in implementing
A sample image-processing application was developed for this article. Both Introduction
Server-side applications such as image-processing and server-logging software perform operations that often involve heavy use of file I/O. For sufficiently large files, memory contention can become an issue. To alleviate this problem, file I/O can now be done by mapping files directly to a process's address space, using
In the sample image-processing application that we developed, the original image is read into memory and then scaled, either reduced or enlarged. Two distinct methods are used to read the file into memory, one method implementing
In the first method, an image file is read completely into memory with a
In the second method, the image file is mapped into the process's address space with the The objectives of this article are:
The following sections describe the concepts involved and the sample application, and also present code examples and generated data. Theoretical Concepts
File I/O is traditionally done with
In general, the
The
When two processes map the same file, segments are created within each process that point to the same
In conventional I/O using the Sample Application
A multithreaded sample application that performs scaling on an image file was developed for this article. The input parameters are the image file to be scaled, an optional output image, and a scaling factor. The image file is read into memory in two ways, as described in the introduction, that is, either with
Using the same input file, we measured and compared the total time taken to read the object into memory (store it in a buffer) in both cases (for The sample application is presented here mainly for illustration purposes. In practice, it may map to an image-processing web application that takes an image file (or a set of image files) as input. The set of image files could either be homogenous (the same file) or heterogeneous (different files). Other input parameters would include a scaling factor. The output of the application would be a set of scaled image files. ImplementationHere we provide descriptions of key implementation details for the sample application, as well as code examples. "read" and "mmap"
In the following example, for the 'type==READ' condition, the image file of interest is opened and a file descriptor is obtained. Memory is then allocated to the image buffer, which is populated by the read call after the
IImageP *
readData (IImageP * dataimg, int type)
{
? IImageP *readimage;
? int w, h;
? int i;
? unsigned int temp;
? unsigned char *ptr;
? char *p;
? char *comments = NULL;
? int maxcolors = 255;
? int greyscale = 0;
? int bytesperpixel = 3;
? int offset = dataimg->offset -1;
? int fildes;
? int whence;
? hrtime_t beginreadData, endreadData ;
? int size = dataimg->width * dataimg->height *
bytesperpixel;
? beginreadData = gethrtime();
?
? if (type==READ) {
??? if ((fildes = open(dataimg->filename, O_RDONLY))
< 0) {
????? fprintf(stderr, "can't open %sn",
dataimg->filename);
?
??? }
??? dataimg->data = (unsigned char *) malloc (size);
??? fprintf(stderr, "in readData: dataimg->data =
%pn", dataimg->data );
??? lseek(fildes, (offset-1), SEEK_SET);
??? read(fildes, dataimg->data, size);
??? close (fildes);
? }
? if (type==MMAP){
?
??? if ((fildes = open(dataimg->filename, O_RDONLY)) < 0) {
????? fprintf(stderr, "can't open %sn",
dataimg->filename);
??? }
?
??? fprintf(stderr, "file = %sn", dataimg->filename);
??? dataimg->data = (unsigned char *)mmap( (caddr_t)0,
size, PROT_READ , MAP_PRIVATE,fildes, 0) + (offset-1) ;
??? fprintf(stderr, "page size = %d", sysconf (_SC_PAGESIZE));
??? if (dataimg->data == MAP_FAILED) {
?fprintf(stderr, "mmap failed");
?perror ("mmap failed due to: ");
?exit(0);
??? }
?
? }
?
? endreadData = gethrtime();
? printf("Total readData() time?? = %lld nsecn",
(endreadData - beginreadData) );
? return dataimg;
}
Scale
A scaling algorithm for reduction is presented here for obtaining a scaled image buffer from an original image buffer, based on pointer arithmetic. This same method is used for both the
IImageP *
scaleImage(int scalingvalue, IImageP * origimage )
{
? hrtime_t beginscaleImage, endscaleImage;
? IImageP *scaledimage;
? unsigned int src_width, src_height, dest_width, dest_height;
? unsigned char *ptr;
? unsigned char *ptrX;
? int scalex, scaley, tempx, tempy;
? int stride;
? int x,y,x2,y2, i, j,k,l;
? beginscaleImage = gethrtime();
? scaledimage = (IImageP *) malloc ( sizeof ( IImageP ) );?
/* need to do a free soon? */
? if (scalingvalue > 0) {
??? scaledimage->width =?? scalingvalue * (origimage->width);
??? scaledimage->height =? scalingvalue * (origimage->height);
??? /* Algorithm for scaling up? */
??? scalex =?? scalingvalue;
??? scaley =?? scalingvalue;
??? stride =? scalex * origimage->width;
? }
? if (scalingvalue < 0) {
??? scaledimage->width =??? (int)
(origimage->width)/(double)abs(scalingvalue);
??? scaledimage->height =? (int)?
(origimage->height)/(double)abs(scalingvalue);
??? /* Algorithm for scaling down? */
??? scalex =??? abs(scalingvalue);
??? scaley =??? abs(scalingvalue);
??? stride =? origimage->width/scalex;?? /*
losing some accuracy here?? */
? }
? scaledimage->offset = origimage->offset;
? scaledimage->filename = origimage->filename;
? scaledimage->comments = origimage->comments;
? scaledimage->data = (unsigned char *) malloc
( scaledimage->width * scaledimage->height * 3 );
? if (scaledimage == NULL) {
???? fprintf (stderr, "Could not create scaled image");
? }
? src_width = origimage->width;
? src_height = origimage->height;
? dest_width =? scaledimage->width;
? dest_height =? scaledimage->height;
? /*? for each pixel in the image, enlargement???? */
? if (scalingvalue > 0) {
??? for ( i = 0; i <? src_height; i++ ) {
????? for ( j = 0; j < src_width; j++ ) {
??????? for (k = 0; k < scaley; k++) {
????????? for (l = 0; l < scalex; l++) {
???? *(scaledimage->data + ((i*scaley + k)*stride) +
(j*scalex) + l) = *(origimage->data + i*src_width + j);
?? }
??????? }
????? }
??? }
? }
? /*? for each pixel in the image, reduction , skipping
some pixels??? */
? if (scalingvalue < 0) {
??? for ( i = 0; i <? src_height; i = i + scalex ) {
????? for ( j = 0; j < src_width; j = j + scaley ) {
???? *(scaledimage->data + ((i/scaley)*stride) +
(j/scalex)) = *(origimage->data + i*src_width + j);
????? }
??? }
? }
? endscaleImage = gethrtime();
? printf("Total ScaleImage() time?? = %lld nsecn",
(endscaleImage - beginscaleImage) );
? return scaledimage;
}
Tests
A 296-MHz, 512-Mbyte Ultra 30 workstation running Solaris 8.0 OE generated the results shown in Table 1 and Figure 1. The performance metric is the total time taken to read an image into a buffer, scale it, and free the image. In the first alternative, this is the time it takes to do
A 24-bit, 3.75-Mbyte, color PPM image (1280 X 1024) was used for testing. Reduction was performed. In Table 1, a factor of two refers to reduction to one-quarter of the original image, that is, half-width by half-height.
Tests were run in alternating order for 10 test cases, that is, Data Nthreads = 10 Nruns = 10 Results
Figure 1 is a plot of the ratio of Table 1: Performance Data for Multithreaded Operation Within a Single Process
Conclusion
We have shown that file I/O performs better with memory mapping than with traditional References
About the AuthorOyetunde Fadele has been at Sun for more than two years. A software engineer in Market Development Engineering (MDE), he works with ISVs in the areas of content management, software tools, and media/image processing. Projects include performance tuning, sizing and scaling, development support, and porting on the Java and Solaris platforms. You can reach him at: oyetunde.fadele@sun.com. September 2002 | ||||||||||||||||||||||||||||||
|
| ||||||||||||