【问题标题】:Why is GUI code so computationally expensive?为什么 GUI 代码的计算成本如此之高?
【发布时间】:2009-02-02 07:57:39
【问题描述】:

你们所有的 Stackoverflowers,

我想知道为什么 GUI 代码会占用很多很多 CPU 周期。原则上,图形渲染远没有 Doom 复杂(尽管大多数公司的 GUI 会引入大量的装饰)。事件处理层似乎也是一个沉重的成本,然而,一个编写良好的实现似乎应该在具有大量内存/缓存的现代处理器上有效地在上下文之间切换。

如果有人在他们的大型 GUI 应用程序或通用 API 本身上运行了分析器,我会对瓶颈所在的位置感兴趣。

可能的解释(我想象的)可能是:

  1. 硬件和应用程序接口之间的高级抽象
  2. 对要执行的正确代码的许多间接级别
  3. 低优先级(与其他进程相比)
  4. 行为不端的应用程序通过调用淹没 API
  5. 过度面向对象?
  6. 在 API 中完成糟糕的设计选择(不仅仅是问题,还有设计理念)

一些 GUI 框架比其他框架好得多,所以我想听听不同的观点。例如,Unix/X11 系统与 Windows 甚至与 WinForms 有很大不同。

编辑:现在是一个社区 wiki - 去吧。我还有一件事要补充——我是学校里的一名算法专家,如果 GUI 代码中存在低效的算法以及它们是什么算法,我会很感兴趣。再说一次,这可能只是实现开销。

【问题讨论】:

  • 我对此也很感兴趣。不能只使用 cpu 渲染 gui,因为如果您的 gfx 卡没有合适的驱动程序,桌面图形渲染速度难以置信。如果您有 gfx 驱动程序,但是 desktop-gfx 运行速度有点快,但永远不会像 directx/opengl 应用程序那样快。
  • 唯一可能很慢的算法是软件渲染算法,但 GUI 正在远离它

标签: user-interface language-agnostic


【解决方案1】:

我一般不知道,但我想在您的列表中添加另一个项目 - 字体渲染和计算。在字体中找到矢量字形并将它们转换为具有抗锯齿的位图表示并非易事。并且通常需要执行两次 - 首先计算文本的宽度/高度以进行定位,然后在正确的坐标处实际绘制文本。

此外,当今的大多数绘图代码都依赖于剪辑机制来仅更新 GUI 的一部分。所以,如果只需要重绘一部分,代码实际上会在后台重绘整个窗口,然后只取需要的部分进行实际更新。

添加:

在 cmets 中我发现了这个:

我对此也很感兴趣。不能仅使用 cpu 渲染 gui,因为如果您的 gfx 卡没有合适的驱动程序,桌面图形渲染速度难以置信。如果您有 gfx-drivers,但是 desktop-gfx 会运行得有点快,但永远不会像 directx/opengl 应用程序那样快。

这就是我所理解的交易:今天的每张显卡都支持通用的绘图界面。我不确定它是否被称为“VESA”、“SVGA”,或者这些只是过去的旧名称。无论如何,这个接口涉及通过中断做所有事情。对于每个像素,都有一个中断调用。或类似的东西。然而,适当的 VGA 驱动程序能够利用 DMA 和其他增强功能,从而减少整个过程的 CPU 密集度。

添加 2: 啊,对于 OpenGL/DirectX - 这是当今显卡的另一个特性。它们针对独占模式下的 3D 操作进行了优化。这就是为什么速度。普通的 GUI 只使用基本的 2D 绘图程序。因此,它每次想要更新时都会发送整个屏幕的内容。然而,3D 应用程序将一堆纹理和三角形定义发送到 VRAM(视频 RAM),然后将它们重新用于绘图。他们只是说“将三角形集#38 与纹理集#25 并绘制它们”。所有这些东西都缓存在 VRAM 中,所以这又快了很多。

我不确定,但我怀疑现代 3D 加速 GUI(Vista Aero、Linux 上的 compiz 等)也可能会利用这一点。他们可以预先将通用位图发送到 VGA,然后直接从 VRAM 中重用它们。然而,任何应用程序绘制的表面仍然需要每次都直接发送以进行更新。

添加 3: 更多创意。 :) 用于 Windows、Linux 等的现代 GUI 是面向小部件的(即面向 Windows 扬声器的控制)。这样做的问题是每个小部件都有自己的绘图代码和相关的绘图表面(或多或少)。当窗口需要重绘时,它会调用其所有子窗口小部件的绘图代码,而这些子窗口小部件又会调用其子窗口小部件的绘图代码等。每个窗口小部件都会重绘其整个表面,即使其中一些被遮挡由其他小部件。使用上面提到的剪辑技术,一些绘制的信息会立即被丢弃,以减少闪烁和其他伪影。但仍然有很多手动绘图代码,包括位图块状、拉伸、倾斜、绘制线条、文本、填充等。所有这些都被转换为一系列 putpixel 调用,这些调用通过剪切过滤器/蒙版和其他过滤器进行过滤东西。啊,是的,Alpha 混合在今天也很流行,因为效果很好,这意味着更多的工作。所以......是的,你可以说这是因为大量的抽象和间接。但是……你真的能做得更好吗?我不这么认为。只有 3D 技术可能会有所帮助,因为它们利用 GPU 进行 alpha 计算和裁剪。

【讨论】:

  • 正如您提到的 80% 是关于渲染的。大多数游戏使用基于多边形的 GUI,因此渲染是 100% 基于视频卡的,并且它比普通 PC GUI 快得多,但是您提到的较新的 GUI 在渲染方面变得更加智能,因此 GUI 性能应该变得微不足道几年
  • 您知道,数据并行性不仅限于 3D 应用程序...您可以使用 2D 做同样的事情,视频卡加速不是一个解决方案,它的基本原理与所说的相同我的程序运行缓慢,买一台更快的电脑。 AAA 游戏需要大量的努力才能快速运行。
  • 我同意。但是,除了求助于所有优秀实践书籍都警告我们的那种编程技巧之外,我看不到任何其他改进方法。您对如何组织基于小部件的 UI 渲染有更好的想法吗?
【解决方案2】:

首先让我们说编写库比编写独立代码要困难得多。您的抽象可在尽可能多的上下文中重用的要求,包括您还没有想到的上下文,这使得即使对于经验丰富的程序员来说,这项任务也具有挑战性。

在库中,编写 GUI 工具包库是一个众所周知的难题。这是因为使用 GUI 库的程序涵盖了非常广泛的领域,具有非常不同的需求。 Mr Why 和 Martin DeMollo discussed the requirements placed of GUI libraries 不久前。

编写 GUI 小部件本身很困难,因为计算机用户对界面行为的微小细节非常敏感。非本地小部件永远不会感觉对,不是吗?为了使非本地小部件正确——事实上,为了使任何小部件正确——您需要花费大量时间来调整行为的细节。

因此,GUI 速度很慢,因为用于创建高度可重用组件的抽象机制引入了低效率,一旦花费了如此多的时间来获得正确的行为,就会增加可用于优化代码的时间的短缺。

【讨论】:

  • +1。我同意你的观点;但这里有两种截然不同的影响。您所说的会导致 GUI 库使用比严格必要的周期更多的周期。然而,我发现 GUI 工具包的粗心/缺乏经验的使用可能仍然会花费更多的性能。
【解决方案3】:

嗯,确实不少。

最简单但可能显而易见的答案是,这些 GUI 应用程序背后的程序员是非常糟糕的程序员。您可以继续编写代码来完成最奇怪的事情,而且速度会更快,但似乎很少有人关心如何做到这一点,或者他们认为这是一项昂贵的无利可图的浪费时间的工作。

直接将计算卸载到 GPU 不一定能解决任何问题。 GPU 就像 CPU 一样,只是它的通用性较低,而更像是一个数据并行处理器。它可以非常出色地进行图形计算。无论您拥有何种图形 API/OS 和驱动程序组合都没有那么重要……好吧,以 Vista 为例,他们改变了桌面组合引擎。这个引擎在只处理那些发生变化的地方要好得多,而且由于 GUI 应用程序的头号瓶颈是重绘,这是一个巧妙的优化策略。这种虚拟化您的计算需求的想法,每次只更新最小的变化。

当窗口需要重绘时,Win32 会向窗口发送 WM_PAINT 消息,这可能是窗口相互遮挡的结果。但是,由窗口本身来确定实际发生了什么变化。更重要的是,没有任何改变,或者所做的改变是微不足道的,以至于它可能只是在你拥有的最上面的表面上进行。

今天不一定存在这种图形处理。我会说人们避免编写真正高效和虚拟化的渲染解决方案,因为收益/成本比率相当低/高(坏)。

Windows Presentation Foundation (WPF) 所做的事情,我认为远优于大多数其他 GUI API 是它将布局更新和渲染更新分成两个单独的通道。虽然 WPF 是托管代码,但渲染引擎不是。渲染发生的情况是托管 WPF 渲染引擎构建一个命令队列(这是 DirectX 和 OpenGL 所做的),然后将其交给本机渲染引擎。这里更优雅的是,WPF 将尝试保留任何不改变视觉状态的计算。如果可以的话,这是一个技巧,可以避免对不必渲染(虚拟化)的东西进行昂贵的渲染调用。

与告诉 Win32 窗口重新绘制自身的 WM_PAINT 相比,WPF 应用程序会检查该窗口的哪些部分需要重新绘制,并且只重新绘制最小的更改。

现在 WPF 不是至高无上的,它是 Microsoft 的坚实努力,但它还不是圣杯......运行管道的代码仍然可以改进,任何托管应用程序的内存占用量仍然超过我想要的.但我希望这是您正在寻找的答案。

WPF 能够以相当不错的方式异步完成一些事情,如果您想制作一个真正响应迅速的低延迟/低 CPU 用户界面,这是非常重要的。异步操作不仅仅是在不同的线程上卸载工作。

总而言之,缓慢且昂贵的 GUI 意味着过多的重绘以及非常昂贵的重绘,即整个表面区域。

【讨论】:

  • WM_PAINT 只表示可能需要在窗口的某些部分进行绘画。 GetUpdateRgn 可让您确定哪些部分需要重绘。大多数复杂的控件都会渲染到位图并在遮挡时重新显示,所以我认为 WPF 没有多大帮助,除了编写糟糕的渲染代码。
【解决方案4】:

我确实在某种程度上依赖于语言。您可能已经注意到,Java 和 RealBasic 应用程序比基于 C(C++、C#、Objective-C)的应用程序要慢一些。

但是,GUI 应用程序比命令行应用程序复杂得多。 Terminal 窗口只需要绘制一个不支持按钮的简单窗口即可。

还有多个循环用于额外的输入和功能。

【讨论】:

    【解决方案5】:

    我认为您可以在 James Gosling(Java 专家,也因其在 X11 之前的窗口方面的工作而闻名系统)。在线获取here[pdf]

    这篇文章关注的是积极的一面(如何让它变快),而不是消极的一面(是什么让它变慢了),但它仍然是一个很好的主题阅读。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-28
      • 1970-01-01
      • 2011-04-24
      • 1970-01-01
      • 2016-03-08
      • 1970-01-01
      • 1970-01-01
      • 2017-09-21
      相关资源
      最近更新 更多