Hello all, sorry for the long message, but I want to get everyone updated with the necessary background before I send my proposed change.

We've long had a trend of duplicate code in CVS, and right now this is definitely the case. There is one area where I think there is enough concensus that we could unify our approaches and cut down some of the duplication, and that's thumbnail generation. It seems every version of thumbnailing uses Epsilon for this except for EFM (because of the "no further dependancies" rule). The point where implementations differ is the method used for generating the thumbnails in the background. There are presently three approaches: 1. block and generate 2. fork for each thumbnail 3. worker thread.

The first approach is simply calling epsilon in the code directly and waiting for the thumbnail to complete. This blocks the UI and can cause usability issues if a large number of images are processed sequentially, and/or the files are very large. On the plus side, it's very simple and easy to code.
Example: emblem

The next method forks a child process when an image is identified, and that child process does the work of calling entropy. When the child exits the parent can identify the child and determine which thumbnail updated. Forking for each thumbnail greatly improves the situation as the UI can continue to process and simply update its image when the thumbnail is ready. It also has the advantage that thumbnailing is accomplished in parallel, while one image is blocking on a read, the previous one can be using the CPU and this scales out to the number of CPU's on the system. The potential problem with this approach is essentially a fork/swap bomb. When there are a large number of images in a directory, you get a process for each image, followed by an immediate attempt to read all of the image data to memory. Process creation is pretty light on Linux, but not on all POSIX compliant systems and there are often requirements on systems to limit the number of simultaneous processes per-user. The default on OS X 10.3 is 100 processes for a single user, thats quite easy to hit if we are forking for a few hundred thumbnails, and the situation gets worse on multi-user systems (think LTSP or SunRay's). We need to handle the case when fork starts to fail.
Examples: exhibit, EFM

The worker thread approach creates a series of worker threads to thumbnail a specific number of thumbnails simultaneously. It has the advantage of the fork method, where it doesn't block the UI while thumbnailing. While allowing some work in parallel, it does not overrun the system by generating all thumbnails simultaneously because there are a limited number of worker threads that are dispatched from a queue of images to thumbnail. The downsides are that it requires more complicated communication mechanisms (and locking) to notify the process when the thumbnails are complete and brings in a pthreads dependancy.
Example: entropy

There is also a race condition performance penalty that applies to all cases as well. If you have two processes generating thumbnails for the same large directory, its entirely possible to reach a state where both processes are generating thumbnails for the same images simultaneously. Eg. I open entropy in my picture directory with 1000 images (just throwing out a convenient number), and I decide to open that directory in exhibit to import some pics. Suppose entropy has processed the first 100 images, and exhibit benefits from all of the previously cached thumbnails so it quickly catches up to entropy, say around image 120. Exhibit sees that no thumbnail exists for image 120 and begins thumbnailing it, meanwhile entropy is just finishing its thumbnail, writes it to disk and moves on to image 121. Exhibit completes its thumbnail of image 120, overwrites the thumbnail entropy wrote, and moves on to image 121. In the worst case, this write-overwrite effect could continue for the remaining 878 images. This is a bit of a simplification because neither forking or worker threads linearly thumbnail like this, but scheduling could result in this type of situation. We've now done twice as much work as necessary, and caused a large spike in IO.

So that's the current state of things, I have a couple of ideas in mind that I'll outline in a follow up message as this one is overly long already.

Nathan

Reply via email to