3
votes

Overview

I am populating a TListView with the ViewStyle set to vsIcon. The Listview is connected to a TImageList where for each item added to the Listview, has its own image as specified by the corresponding index.

The idea is to be able to automate the process of manipulating a series of bitmaps all at once. Each Bitmap is different, although always the same in size.

Due to the nature of how this works there is never a fixed size or limit as to how many Bitmaps are been added to the ImageList, the only restriction is the available System memory.

Problem

The issue I have is related to the performance of the manipulation on these Bitmaps. By manipulation I mean performing different Image processing techniques on the Bitmaps such as Greyscale, Swapping colors, Adjusting Brightness etc.

Now suppose it takes 3 seconds to Adjust the Brightness of a Bitmap that is 1Mb in size. If the ImageList has a total of 10 Bitmaps then this process now takes approx 30 seconds.

(Note: I have not tested the speed with GetTickCount or anything, these are just examples).

Consider the fact though that as I said earlier this ImageList could be anything in size, potentially the processing time could go on for what may seem like an eternity.

When I perform any manipulation on these Bitmaps I use GetBitmap inside a loop to send each Bitmap to an off screen buffer Bitmap to perform the manipulation on, like this:

var
  Bmp: TBitmap;
  i: Integer;
begin
  Bmp := TBitmap.Create;
  try
    ImageList1.BeginUpdate;
    try
      for i := 0 to ImageList1.Count - 1 do
      begin
        ImageList1.GetBitmap(i, Bmp);
        Bmp.PixelFormat := pf24Bit;
        // perform manipulation to Bmp here
        ImageList1.Replace(i, Bmp, nil);
      end;
    finally
      ImageList1.EndUpdate;
    end;
  finally
    Bmp.Free;
  end;
end;

Run that over a ImageList that could contain any size or amount of Images and you can maybe understand how this could be slow.

I am looking for ways to optimize and improve the way of doing this as right now it is no where near acceptable performance wise. BeginUpdate and EndUpdate does not offer a worthwhile solution here. I am not looking for any miracles as I understand that most computations require lengthy processing time, I just need to reduce this time as best as possible with any help and advice you may have to offer.

2
I'll have to say that Multi-threading is the way you need to go, but not just any, you need a thread pool. The OmniThreadLibrary has great resources to do so, look here: otl.17slon.com And don't do like I attempted once and try to create over 700 threads at the same time (assuming 700 images) - as this will literally kill your computer. The idea of a thread pool is to run, let's say, 5 threads at once while the rest stay in queue waiting. Once one thread finishes, another one is fired, so there are presumably never more than 5 threads running at a time.Jerry Dodge
@JerryDodge by threads do you mean utilising each available core on a cpu?user1175743
No, he means using TThreads. These will run concurrently, allowing you to process multiple images at the same time, while your application UI will remain responsive. In theory, the OS will dole out the threads to alternate cores if they're available, you shouldn't have to worry about that.Tim Sullivan
@TimSullivan thanks for clearing that upuser1175743
I would say put a thread limit of 20-30 and on a good machine maybe 50, but when working with hundreds of bitmaps at once, you need to think about the computer's performance. Perhaps calculate the available memory vs the size of each image you intend to process and create your thread count based on available memory.Jerry Dodge

2 Answers

4
votes

Personally, I'd do a few things:

0) Don't do anything until you've profiled your code to make sure this is in fact where the slowdown is happening.

1) Rather than using a TImageList, I'd use a TList descendent to store the images. I'm not sure if this would have a direct impact on performance, but IIRC, TImageList relies a lot on the built-in Windows image handling, which might be slower.

2) Update the images on-demand rather than all together, if this is possible.

3) Thread the conversion process rather than run it in the main thread. This is pretty simple if you're also using a TList, as you can just pass the list item to the thread (or thread queue). This has the added benefit of using multiple processors if they're available.

Threading has the most potential to improve perceived performance of your app, even if it might not actually take less time. Combine that with as-required conversion and you should see a giant improvement.

ETA: A thread pool, as mentioned by Jerry in the comment, is a good idea. There are some examples of this on the Embarcadero site, if you search their blogs.

1
votes

Added to the excellent suggestions of Tim: I am not sure how you access your bitmaps, use the ScanLine property of the bitmap if you did not do so. And use pf32Bit if possible, access is easier and mostly even faster.

Be aware that a bitmap is not threadsafe. It is no problem to read a scanline, but when you want to write the results back be sure you use a critical section or something like that.

Using a profiler is highly recommended, you will be surprised what code is inefficient or not. I use ProDelphi: it is not expensive and very precise.