Pixel Access & L1 Cache Misses

Magick++ is an object-oriented C++ interface to ImageMagick. Use this forum to discuss, make suggestions about, or report bugs concerning Magick++.
Post Reply
insomniacp
Posts: 11
Joined: 2012-08-20T09:03:33-07:00
Authentication code: 67789

Pixel Access & L1 Cache Misses

Post by insomniacp »

Hello once again,
I have code that accesses an image's pixels. I have noticed using caching tools that when accessing the pixels it causes L1 and usually also an L3 cache misses quite frequently even after simply incrementing the PixelPacket pointer. This is in a very often run loop so its getting up to 2,550,650 L1 misses and 2,439,353 L3 misses which is killing the execution time of the program. That leads me to believe that the given pixel cache is located in different memory than the actual data. Is it possible to get direct access to an Images pixel data since that will be in contiguous memory? Yes I understand that is not "safe" for most users which is why it probably isn't available. If it is not available could you inform me how to code it myself into magick and magick++? I looked at the code and I can't find where the pixels are stored exactly in image.c. Are the stored in PixelPacket format in some array/pointer somewhere that I could simply return it through magick into magick++ so I can make use of it? I need the entire image data so I am currently doing

Code: Select all

data.cache = src.getConstPixels(0,0,data.sFullW,data.sFullH);
which is getting the entire set of pixels from the src Image.

Thanks for all the help so far.
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Pixel Access & L1 Cache Misses

Post by magick »

In most cases ImageMagick maps the image pixels contiguously in memory and getConstPixels() returns a pointer to the memory which should ensure good cache performance. The last time we measured cache performance, we got few cache misses. Heap memory is allocated if the image includes a clip mask or if the memory resources are exhausted. To debug, first print the pointer returned from getConstPixels(). Does it show spatial locality? Next set the MAGICK_DEBUG environment variable to CACHE. Does the debugging show the cache is allocated in heap? Or on disk?

Does data.sFullW and data.sFullH return the entire image? If it exceeds the image size, we would expect cache misses. Try getting one scanline at a time. Also make sure you measure cache misses in a tight loop in a simple program that just reads an image and then loops over it. That will ensure that any cache misses rightly belong to ImageMagick rather than your code set.
insomniacp
Posts: 11
Joined: 2012-08-20T09:03:33-07:00
Authentication code: 67789

Re: Pixel Access & L1 Cache Misses

Post by insomniacp »

Is there any way to get direct access to the cache without making a copy of the pixel data? I have the following code and it takes 25.5ms to get the cache from a 1900x1900 image.

Code: Select all

dest.modifyImage();
Pixels viewFinal(dest);
PixelPacket* finalPixels = viewFinal.get(0,0,dest.columns(), dest.rows());
The image is loaded from an mpc file. What are the reasons it would take 25ms to get the pixel data? This is the slowest part of my code taking longer than a resize function shrinking the image to 380x440 (14.8ms) so this is a significant problem for me. Is there a way to bypass the copying of pixels so I get direct write access to the image pixels or a faster way to get the pixel data? I have wondered about manipulating the getConst() pointer to allow me to read and write to that memory location but it segfaults. I haven't been able to discern much from the magick library code yet either. I don't have any other pixel caches and things like that so I know I should be able to safely write straight to a pixel cache but it seems things are setup to prevent that.

EDIT:
For example could I go into image.c or cache.c (where the other functions are) add a function

Code: Select all

MagickExport PixelPacket *GetCompletePixels(Image *image)
{
 CacheInfo
    *cache_info;
 assert(image != (Image *) NULL);
  assert(image->signature == MagickSignature);
  assert(image->cache != (Cache) NULL);
  cache_info=(CacheInfo *) image->cache;
  return cache_info->pixels;
}
and add a wrapper function into Image.h/cpp?
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Pixel Access & L1 Cache Misses

Post by magick »

The fast access to image pixels with Magick++ is getConstPixels() assuming your region request is within the bound of the image. getConstPixels() creates / throws / destroys exceptions each time its called. If you call getConstPixels() many times you could benefit from setting up an exception once and calling
  • const PixelPacket* p = (*GetVirtualPixels)( constImage(),
    x_, y_,
    columns_, rows_,
    &exceptionInfo );
directly.

We set up a small MagickCore API program that calls GetVirtualPixels() reads a 1900x1900 MPC image and it ran in 4ms. That includes creating an exception, an image info structure, reading the image, accessing the pixel region, and destroying the exception and image info structure. We're using ImageMagick 6.7.9-2.
insomniacp
Posts: 11
Joined: 2012-08-20T09:03:33-07:00
Authentication code: 67789

Re: Pixel Access & L1 Cache Misses

Post by insomniacp »

Is there a non const method for this? I will be needing to edit the pixel colors in some cases and these are the slow ones. getting a const pointer is near instant I agree. I implemented my own function but it seems cache->pixels doesn't allow for writing and gives a seg fault when I write to it but I can read from that pointer. My original question was about a const pointer so I understand the confusion on it but my current question is on getting a non-const pointer to make changes to the colors. I don't mind getting a straight unsigned char* or Quantum* and manually looping over it if that is how it is stored in the image class/struct.

Thanks for the help so far.

Edit:
Basically when I read in a file I want to know where the pixel data is stored in the image class/struct so I can get that data directly (yes, not completely safe).
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Pixel Access & L1 Cache Misses

Post by magick »

Use getPixels(). It gives you direct access to the pixels in memory and they can be modified. Don't forget to call syncPixels() to ensure modified pixels are updated in the pixel cache.
insomniacp
Posts: 11
Joined: 2012-08-20T09:03:33-07:00
Authentication code: 67789

Re: Pixel Access & L1 Cache Misses

Post by insomniacp »

I have tried that but it is taking upwards of 25milliseconds to complete which I think is too long since it is about half the execution time of my program. I am hoping there is a faster method that would allow me to to edit the pixels. The fastest method would be a direct pointer to the pixels in memory where they live in the image struct but I can't find where they are. My application loads two images, multiplies them together and then resizes the result. I find it difficult to be okay with half the time being used loading a pixel array that should already be in memory. Is there a way around using the getPixel and syncPixel commands and just get the original array of pixels for direct modification (IE no need to sync cause the changes are made directly on the pixels in the image).
User avatar
magick
Site Admin
Posts: 11064
Joined: 2003-05-31T11:32:55-07:00

Re: Pixel Access & L1 Cache Misses

Post by magick »

MPC images are read-only memory mapped. getConstPixels() returns a pointer to the memory map. However, getPixels() must clone the image first since its a read-only memory map and cannot be updated. The cloning is most likely what is consuming most of your 25ms. How long does it take for a non-MPC image format. Try MIFF instead of MPC.

A fix might be to memory map MPC as read / write. That way a clone of the image is not necessary when you update the pixels. We don't do that now because it any changes to the pixels would update the file on disk since its memory-mapped.
insomniacp
Posts: 11
Joined: 2012-08-20T09:03:33-07:00
Authentication code: 67789

Re: Pixel Access & L1 Cache Misses

Post by insomniacp »

That would explain it. I guess the simplest/best solution would be to generate a blank image and multiply the two images together and place the result on this blank image. That would allow me to have two const pointers which are fast and generating the new image doesn't take very long (if I recall previous code I have written properly) and would give me the needed in memory cache without altering the saved file or having to clone the image.
Post Reply