Delphi 如何在同时处理多个位图的同时提高性能?

Delphi 如何在同时处理多个位图的同时提高性能?,delphi,delphi-xe,Delphi,Delphi Xe,概述 我正在使用设置为vsIcon的ViewStyle填充TListView。Listview连接到一个TImageList,其中对于添加到Listview的每个项目,都有相应索引指定的自己的图像 其想法是能够一次自动操作一系列位图的过程。每个位图都不同,但大小始终相同 由于其工作原理的性质,对于向ImageList添加的位图数量,从来没有固定的大小或限制,唯一的限制是可用的系统内存 问题 我遇到的问题与这些位图上操作的性能有关。通过操作,我指的是在位图上执行不同的图像处理技术,如灰度、交换颜色

概述

我正在使用设置为vsIcon的ViewStyle填充TListView。Listview连接到一个TImageList,其中对于添加到Listview的每个项目,都有相应索引指定的自己的图像

其想法是能够一次自动操作一系列位图的过程。每个位图都不同,但大小始终相同

由于其工作原理的性质,对于向ImageList添加的位图数量,从来没有固定的大小或限制,唯一的限制是可用的系统内存

问题

我遇到的问题与这些位图上操作的性能有关。通过操作,我指的是在位图上执行不同的图像处理技术,如灰度、交换颜色、调整亮度等

现在假设调整1Mb大小的位图的亮度需要3秒钟。如果ImageList总共有10个位图,则此过程现在大约需要30秒

(注意:我没有使用GetTickCount或其他工具测试速度,这些只是示例)。

请考虑这样一个事实,正如我前面所说,这个图像列表可以是任何大小的,但处理时间可能会持续到似乎是永恒的

当我对这些位图执行任何操作时,我在循环中使用GetBitmap将每个位图发送到屏幕外缓冲区位图以执行操作,如下所示:

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;
在可以包含任意大小或数量图像的ImageList上运行,您可能会理解这是多么缓慢


我正在寻找方法来优化和改进这样做的方式,因为目前它在性能方面还远远不能令人接受
BeginUpdate
EndUpdate
在这里不提供有价值的解决方案。我不是在寻找奇迹,因为我知道大多数计算需要很长的处理时间,我只是需要尽可能地减少这一时间,并提供您可能需要的帮助和建议。

就我个人而言,我会做几件事:

0)在对代码进行概要分析之前,不要做任何事情,以确保这实际上是发生减速的地方

1) 我不使用TImageList,而是使用TList子代来存储图像。我不确定这是否会对性能产生直接影响,但IIRC,TImageList非常依赖内置的Windows映像处理,这可能会更慢

2) 如果可能的话,按需更新图像,而不是全部更新

3) 线程化转换进程,而不是在主线程中运行它。如果您还使用TList,这非常简单,因为您可以将列表项传递给线程(或线程队列)。如果可以使用多个处理器,这还有额外的好处

线程化最有可能提高应用程序的感知性能,即使它实际上可能不会花费更少的时间。将其与所需的转换相结合,您将看到巨大的改进


ETA:正如Jerry在评论中提到的,线程池是个好主意。有,如果你搜索他们的博客。

添加到Tim的优秀建议中:我不确定你如何访问位图,如果你没有这样做,请使用位图的
ScanLine
属性。如果可能的话,使用PF32位,访问更容易,甚至更快

请注意,位图不是线程安全的。读取扫描线是没有问题的,但是当您想要写回结果时,请确保使用关键部分或类似的内容


强烈建议使用探查器,您会惊讶于代码是否低效。我使用ProDelphi:它不昂贵而且非常精确。

我不得不说,多线程是您需要的方式,但不仅仅是任何一种,您需要一个线程池。OmniThreadLibrary有大量的资源来实现这一点,请看这里:不要像我曾经尝试过的那样,同时尝试创建700多个线程(假设700个图像),因为这实际上会杀死你的计算机。线程池的概念是一次运行5个线程,而其余线程则在队列中等待。一旦一个线程完成,就会触发另一个线程,因此一次运行的线程可能不会超过5个。@Jerrydoge by threads你的意思是利用cpu上的每个可用内核吗?不,他的意思是使用
TThread
s。它们将同时运行,允许您同时处理多个图像,而应用程序UI将保持响应。理论上,如果有可用的内核,操作系统会将线程分配给备用内核,你不必担心。@TimSullivan感谢你澄清这一点,upI会说,将线程限制为20-30,在一台好的机器上可能是50,但当同时处理数百个位图时,你需要考虑计算机的性能。也许可以计算可用内存与要处理的每个图像的大小,并根据可用内存创建线程数。使用
t列表
肯定会提高性能(即使是毫秒),因为正如Tim提到的,
t列表
非常重。但是,这会降低列表项自动显示其链接图像的能力。这意味着您需要手动绘制这些图像。仍有可能,但涉及面更广。尽管如此,我还是强烈推荐这样做,加载/处理要在列表中显示的图像并不是一件容易的事情,应该小心处理。具有讽刺意味的是,就在今天,我被赋予了这样的任务(仅在网格中而不是在
TListView
中),将处理放入线程似乎是我需要做的。当然,即使使用Application.ProcessMessages,在主线程上运行显然也是缓慢和无响应的。将图像存储在TList中也可能比ImageList更好。泰斯