【问题标题】:How to improve performance whilst working on several bitmaps at once?如何在同时处理多个位图时提高性能?
【发布时间】:2012-05-29 23:45:15
【问题描述】:

概述

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

我们的想法是能够一次性自动处理一系列位图。每个位图都不同,但大小始终相同。

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

问题

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

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

(注意:我没有用 GetTickCount 或任何东西测试过速度,这些只是示例)。

请考虑这样一个事实,尽管正如我之前所说,这个 ImageList 可以是任何大小,但处理时间可能会持续很长时间。

当我对这些位图执行任何操作时,我在循环中使用 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 上运行它,您可能会理解这可能会很慢。

我正在寻找优化和改进执行此操作的方法的方法,因为目前它在性能方面还远未达到可接受的水平。 BeginUpdateEndUpdate 在这里没有提供有价值的解决方案。我不是在寻找任何奇迹,因为我知道大多数计算都需要很长的处理时间,我只需要在您可能需要提供的任何帮助和建议下尽可能减少这段时间。

【问题讨论】:

  • 我不得不说多线程是你需要走的路,但不仅仅是任何一个,你需要一个线程池。 OmniThreadLibrary 有很好的资源,请看这里:otl.17slon.com 不要像我曾经尝试过的那样尝试同时创建 700 多个线程(假设有 700 个图像) - 因为这实际上会杀死您的计算机。线程池的想法是一次运行 5 个线程,而其余的则留在队列中等待。一旦一个线程完成,就会触发另一个线程,因此一次运行的线程可能不会超过 5 个。
  • @JerryDodge by threads 你的意思是利用 cpu 上的每个可用内核吗?
  • 不,他的意思是使用TThreads。这些将同时运行,允许您同时处理多个图像,而您的应用程序 UI 将保持响应。从理论上讲,如果可用,操作系统会将线程分配给备用内核,您不必担心。
  • @TimSullivan 感谢您的澄清
  • 我会说将线程限制设置为 20-30,在一台好机器上可能是 50,但是当同时处理数百个位图时,您需要考虑计算机的性能。也许计算可用内存与您打算处理的每个图像的大小,并根据可用内存创建线程数。

标签: delphi delphi-xe


【解决方案1】:

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

0) 在您分析您的代码以确保这确实是减速发生的地方之前,不要做任何事情。

1) 我不使用 TImageList,而是使用 TList 后代来存储图像。我不确定这是否会对性能产生直接影响,但是 IIRC、TImageList 很大程度上依赖于内置的 Windows 图像处理,这可能会更慢。

2) 如果可能的话,按需更新图像,而不是一起更新。

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

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

ETA:Jerry 在评论中提到的线程池是个好主意。有some examples of this on the Embarcadero site,如果你搜索他们的博客。

【讨论】:

  • 使用TList 肯定会提高性能(即使提高一毫秒),因为正如蒂姆所提到的,TImageList 非常重。但是,这会削弱列表项自动显示其链接图像的能力。这意味着您需要手动绘制这些图像。仍然可能,但涉及更多。我仍然强烈推荐这个,加载/处理要在列表中显示的图像并不是一件容易的事,应该小心处理。具有讽刺意味的是,就在今天,我被赋予了执行此操作的任务(仅在网格中,而不是 TListView
  • 将处理放入线程似乎是我需要做的。当然,即使使用 Application.ProcessMessages,在主线程上运行显然也很慢且无响应。将图像存储在 TList 中也可能比 ImageList 更好。这些都是我需要研究以更好地了解如何实施它们的所有事情,这是我需要自己完成工作的问题之一。我知道速度变慢是因为修改了无限数量的位图,因为在一个位图上执行相同的操作相对较快。
【解决方案2】:

补充了 Tim 的优秀建议:我不确定您如何访问您的位图,如果您没有这样做,请使用位图的 ScanLine 属性。如果可能,请使用 pf32Bit,访问更容易,而且速度更快。

请注意位图不是线程安全的。读取扫描线是没有问题的,但是当你想写回结果时,一定要使用临界区或类似的东西。

强烈建议使用分析器,您会惊讶于哪些代码效率低下。我使用 ProDelphi:它不贵而且非常精确。

【讨论】:

  • 为什么 32 位位图 更容易访问 并且更快?你能详细说明一下吗?没有什么能比得上读取扫描线了,扫描线只给你一个指向原始像素数据的指针,你可以直接用它来操作。
  • @Arnold 他的意思是你不一定能自己读取扫描线并检索图像数据的实际副本。相反,它只是指向可以找到该图像数据的一块内存。
  • PS - 我希望这是您之前评论开头的意外错字...
  • @TLama - 我做了一些实验,发现 32 位通常比 24 位快。浮现在脑海中的一种解释是以四个或八个单词为一组的记忆组织。与 24 位相比,32 位需要更少的取指。我不确定直接操作扫描线是什么意思:它仍然需要读取、操作和写入扫描线的元素。还是我错过了什么?。
  • @TLama - 很抱歉打错字了,杰瑞 - 谢谢你指点我
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-11
  • 2014-09-20
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多