为什么GUI代码计算成本如此之高?

14

各位Stackoverflower们,

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

如果有人对他们的大型GUI应用程序或常用API运行过分析器,我对瓶颈所在感兴趣。

可能的解释(我想象的)可能包括:

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

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

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


我也对此非常感兴趣。如果你没有适当的显卡驱动程序,那么 GUI 渲染肯定不会仅使用 CPU,因为桌面图形渲染会非常缓慢。但如果你有了显卡驱动程序,桌面图形就会运行得相对较快,但永远无法像 DirectX/OpenGL 应用程序那样快。 - Orange
唯一可能较慢的算法是软件渲染算法,但GUI正在逐渐摆脱这种情况。 - Robert Gould
5个回答

9
我不是专家,但我想在你的列表中再添加一个项目 - 字体渲染和计算。找到字体中的矢量字形并将其转换为带有抗锯齿的位图表示形式并不是一项小任务。通常需要做两次 - 首先计算文本的宽度/高度以进行定位,然后在正确的坐标处实际绘制文本。
此外,大多数绘图代码今天都依赖于剪切机制来更新GUI的部分内容。因此,如果只需要重新绘制一个部分,代码实际上会在幕后重新绘制整个窗口,然后只取所需的部分进行实际更新。
在评论中,我发现了这个:
“我也非常感兴趣。如果您的图形卡没有适当的驱动程序,则桌面图形呈现非常慢。但是,如果您有gfx驱动程序,则桌面gfx会变得很快,但永远不会像直接x / opengl应用程序那样快。”
就我所知,今天所有的图形卡都支持用于绘图的通用接口。我不确定它是否称为“VESA”,“SVGA”或者这些只是过去的旧名称。无论如何,该接口涉及通过中断执行所有操作。对于每个像素,都有一个中断调用。或者是这样的什么东西。但是,适当的VGA驱动程序可以利用DMA和其他增强功能,使整个过程大大减少CPU使用率。
哦,还有OpenGL / DirectX - 这是今天图形卡的另一个特性。它们经过优化,可以在独占模式下进行3D操作。这就是速度的原因。普通GUI只利用基本的2D绘图过程。因此,每次需要更新时,它都要发送整个屏幕的内容。然而,3D应用程序会向VRAM(视频RAM)发送一堆纹理和三角形定义,然后只需重复使用它们进行绘制。他们只说类似于“使用纹理集#25的三角形集#38并将它们绘制出来”。所有这些内容都被缓存在VRAM中,所以这又快了。
我不确定,但我认为现代的3D加速GUI(Vista Aero,在Linux上的compiz等)也可能利用这一点。他们可以预先向VGA发送常见的位图,然后直接从VRAM中重复使用它们。但是,任何应用程序绘制的表面仍然需要每次直接发送以进行更新。 添加3: 更多想法。:) 现代的Windows、Linux等GUI都是基于小部件的(对于Windows用户来说,这意味着控件导向)。问题在于每个小部件都有自己的绘制代码和相关的绘制表面(或多或少)。当窗口需要重新绘制时,它会调用所有子小部件的绘制代码,子小部件又会依次调用它们子级别的绘制代码,以此类推。即使一些区域被其他小部件遮挡,每个小部件仍然要重新绘制它的整个表面。通过上述提到的剪辑技术可以立即丢弃一些绘制信息以减少闪烁和其他伪像。但仍然需要大量手动的绘图代码,包括位图混合、拉伸、扭曲、画线、文字、泛洪填充等等。所有这些都转化为一系列经过剪辑过滤器/掩码和其他东西过滤的putpixel调用。啊,是的,Alpha混合也变得越来越流行了,以获得漂亮的效果,这意味着更多的工作。所以...你可以说这是因为有很多抽象和间接性。但是...你真的能做得更好吗?我不这么认为。只有3D技术可能有所帮助,因为它们利用GPU进行Alpha计算和剪辑。

正如你所提到的,80% 的问题在于渲染。大多数游戏使用基于多边形的 GUI,因此渲染完全依赖于显卡,比普通 PC GUI 快得多,但正如你所提到的,新的 GUI 正变得越来越聪明,因此 GUI 性能应该在未来几年内变得微不足道。 - Robert Gould
你知道,数据并行不仅限于3D应用程序...你可以在2D上做得很好,视频卡加速不是解决方案,这本质上与说我的程序运行缓慢,买一台更快的计算机是相同的理由。AAA游戏标题需要大量的努力才能快速运行。 - John Leidegren
我同意。但除了采用所有好的编程实践书籍警告我们不要使用的那种编程技巧之外,我没有看到其他改进事物的方式。你有更好的想法来组织基于小部件的UI渲染吗? - Vilx-

5
让我们先说写库比编写独立代码要难得多。你的抽象化需在尽可能多的情境下复用,包括你尚未想到的情境,这使任务即便对经验丰富的程序员而言也很具挑战性。
在所有的库中,编写GUI工具包库是一个著名的难题。这是因为使用GUI库的程序在不同领域和需求上有广泛的差异。Mr Why 和 Martin DeMollo 曾在不久前讨论过GUI库的需求
编写GUI小部件本身就很困难,因为电脑用户对界面行为的微小细节非常敏感。非原生小部件总感觉不舒服,是吧?实际上,为了正确呈现任何小部件,你需要花费大量时间来调整它的行为细节。
因此,由于创建高度可重用组件所使用的抽象机制引入的低效率以及用于优化代码的时间缺乏,GUI变得缓慢。

我同意你的观点;但是这里有两个非常不同的影响因素。你所说的导致GUI库使用的周期比严格必要的周期更多。然而,我发现没有考虑周全/缺乏经验的GUI工具包的使用可能仍会造成更多的性能损失。 - mghie

2

嗯,这是相当多的内容。

最简单但也可能是显而易见的答案是,这些GUI应用程序背后的程序员是非常糟糕的程序员。你可以在编写执行最奇怪的事情的代码时走得更远,它会更快,但很少有人关心如何做到这一点,或者他们认为这是一项代价高昂、无利可图的浪费时间的努力。

为了让事情明朗化,将计算卸载到GPU上并不一定能解决任何问题。GPU就像CPU一样,只不过它具有更少的通用性和更多的数据并行处理器。它可以非常出色地进行图形计算。无论你使用什么图形API/操作系统和驱动程序组合,都不太重要...好吧,以Vista为例,他们改变了桌面组成引擎。这个引擎更好地完成了只合成已经发生变化的部分,而由于GUI应用程序的首要瓶颈是重新绘制,这是一个整洁的优化策略。这种虚拟化计算需求,每次只更新最小的变化。

Win32在需要重新绘制窗口时向窗口发送WM_PAINT消息,这可能是窗口互相遮挡的结果。然而,窗口本身需要弄清楚实际发生了什么变化。更多情况下,没有任何改变,或者所做的改变是微不足道的,以至于它本来可以直接在你拥有的任何最顶层表面之上执行。

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

Windows Presentation Foundation(WPF)做的一件事,我认为比大多数其他GUI API都要优秀,就是将布局更新和渲染更新分成两个单独的步骤。虽然WPF是托管代码,但渲染引擎不是。关于渲染发生了什么,是托管的WPF渲染引擎构建命令队列(这就是DirectX和OpenGL所做的),然后将其移交给本机渲染引擎。在这里更加优雅的是,WPF将尝试保留任何未更改视觉状态的计算。如果可能的话,这是一个技巧,可以避免为不必要的事情进行昂贵的渲染调用(虚拟化)。

与告诉Win32窗口自我重绘的WM_PAINT相反,WPF应用程序将检查哪些部分需要重新绘制,并仅重新绘制最小的更改。

现在WPF并不是至高无上的,它是微软的一个坚实的努力,但还不是圣杯...运行管道的代码仍然可以改进,任何托管应用程序的内存占用仍然比我想要的多。但我希望这就是你要找的答案。

WPF能够相当不错地异步执行一些操作,如果你想要制作一个真正响应快、低延迟/低CPU的UI,那就是一个巨大的问题。异步操作不仅仅是在不同


WM_PAINT只是表示窗口的某些部分可能需要进行绘制。GetUpdateRgn可以帮助您确定哪些部分需要重新绘制。大多数复杂的控件都会渲染到位图上,并在遮挡时进行回刷,因此我认为WPF并没有太大的帮助,除非是针对糟糕编写的渲染代码。 - SmacL

0

这在某种程度上取决于编程语言。您可能已经注意到,Java和RealBasic应用程序比它们基于C的(C ++,C#,Objective-C)对应程序慢得多。

然而,GUI应用程序比命令行应用程序复杂得多。终端窗口只需要绘制一个不支持按钮的简单窗口。

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


0

我认为你可以在James Gosling的《窗口系统设计:如果我能在2002年重新设计》中找到一些有趣的想法(这位Java大神也因其在X11之前的窗口系统上的工作而闻名)。可在这里[pdf]在线获取。

该文章重点关注正面方面(如何使其更快),而不是负面方面(是什么使其变慢),但仍然是这个主题上的好读物。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接