游戏引擎学习第112天

[复制链接]
发表于 2025-10-29 14:16:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
黑板:优化

本日的内容是关于优化的,紧张讨论了怎样在开发中进步代码的服从,尤其是当游戏的帧率出现题目时。优化并不总是要将代码做到最快,而是要确保代码充足高效,以制止性能题目。优化的过程是一个反复迭代的过程,目标是找到一个“充足好”的办理方案,而不是寻求极致优化。
优化的第一步并不是直接优化代码,而是要举行丈量和分析。这一步很紧张,由于只有相识代码的体现和瓶颈,才气有效地举行优化。丈量代码的性能,确定哪些部门的性能须要提升,是乐成优化的关键。
在优化过程中,起紧张相识内存访问资本、指令周期以及怎样公道使用CPU和GPU的性能。优化的目标是通过公道的筹划和改进,使得步调的服从到达预期目标,而不肯定是将其推到极限。
固然不涉及非常深入的技能细节,如缓存行别名等高级优化,但会先容一些通用的优化方法,实用于大多数场景。对于那些对优化有更深入爱好的人,可以继续学习这些更高级的本事和理论。
总的来说,优化是一个平衡的过程,须要根据需求做出公道的调解,目标是进步性能并确保开发进度。
黑板:CPU + GPU 指令

在盘算机中,CPU和GPU是两个紧张的处置处罚单元,它们实行游戏中的每一帧利用,且每每是同时工作的。它们都属于芯片,通常焊接在主板上或作为独立显卡存在。这些芯片的工作原理是解码一系列的指令流,也就是一组按特定格式分列的字节指令。每条指令会让处置处罚器实行某种盘算或利用。
这些指令可以分为两大类:一类是加载(load)和存储(store)指令,它们涉及内存的读写利用。加载指令从内存中获取数据并将其放入处置处罚器的寄存器或缓存中举行利用,而存储指令则是将盘算结果写回内存。
另一类指令是算术逻辑单元(ALU)指令,这类指令实行各种盘算使命,好比加法、减法、乘法等,直接影响游戏中的逻辑处置处罚。通过这些指令,CPU和GPU可以或许实行复杂的盘算和数据利用,从而推动游戏的运行。
黑板:广义数学运算(SIMD)

今世CPU和GPU的处置处罚方式通常依赖于一种称为SIMD(单指令多数据)的技能。这意味着处置处罚器通过一条指令同时对多个数据举行利用,而不是逐个数据实行类似的利用。通常情况下,最根本的SIMD利用是处置处罚四个数据项,现实上很多时间会处置处罚更多数据,好比8个、16个乃至64个数据项。
举个例子,当我们写出类似“float x = y + 3;
”的代码时,现实实行过程中,CPU或GPU并不会单独处置处罚每一个数据项。它们会同时对多个数据项实行加法利用,比方四个 y 值加上 3,而且大概会扬弃除一个结果外的其他盘算结果。这种做法的缘故因由在于,今世处置处罚器的筹划就是为了通过一次指令处置处罚多个数据项,从而进步盘算服从。
这种筹划的目标是由于解码和处置处罚这些指令流并非无代价。处置处罚器在处置处罚这些指令时须要举行大量的工作,尤其是在实行数学运算时,使用SIMD技能可以大幅进步处置处罚器的服从,使其可以或许在更短的时间内完成更多的盘算。通过这种方式,处置处罚器可以或许淘汰指令解码和数据处置处罚的开销,从而进步团体性能。
“四个 y 值加上 3” 的意思是,今世CPU或GPU在处置处罚某些利用时并不会像我们在代码中看到的那样单独处置处罚每个数据项。它们通常会一次性处置处罚多个数据项。
以加法运算为例,如果有以下代码:
  1. float x = y + 3;
复制代码
如果 y 是一个数组或多个数据项,而不是一个单一的值,今世处置处罚器会一次性将多个 y 的值与 3 相加。详细来说,处置处罚器大概会一次性将 y[0]、y[1]、y[2] 和 y[3] 四个值分别加上 3,并将结果保存在一个新的数据容器中。如许,处置处罚器会同时对多个 y 值举行加法利用,而不是像传统的单个运算那样按次序逐一处置处罚。
这种方式是通过 SIMD 技能实现的,意味着同一条指令同时作用于多个数据点,从而加快盘算过程。比方,处置处罚器大概会同时盘算 y[0] + 3、y[1] + 3、y[2] + 3 和 y[3] + 3,然后将这四个结果放入一个寄存器或内存位置。这种处置处罚方式大大进步了盘算服从。
黑板:一个示例指令

在处置处罚器中,一条指令通常涉及利用寄存器的内容。比方,大概会有一条指令要求将 r1 和 r2 寄存器中的内容相加,并将结果存储到 r0 寄存器中。这条指令的利用就是盘算 r0 = r1 + r2。
寄存器是处置处罚器用来存储数据的高速存储地域,通常是处置处罚器工作时用来存放最常用的数据聚集。每个处置处罚器有差别数目标寄存器,通常是16个或更多。指令流畅常包罗从内存加载数据到寄存器,举行利用,然后将结果从寄存器写回内存。
比方,一条完备的指令流大概如下:

  • 从内存加载一个值到 r1 寄存器。
  • 从内存加载另一个值到 r2 寄存器。
  • 实行加法利用,将 r1 和 r2 中的值相加,并将结果存储到 r0 寄存器中。
  • 将 r0 中的结果写回到内存。
这只是一个简朴的指令流示例,目标是资助明白在处置处罚器内部现实上是怎样实行这些盘算的。每条指令都会涉及从内存读取数据、在寄存器中处置处罚数据,并终极将结果写回内存的过程。
黑板:发出指令是昂贵的

处置处罚器解码指令流的过程非常复杂。起首,处置处罚器须要选择得当的寄存器来举行加法运算,并确保指令之间的依赖关系得到正确处置处罚。比方,如果某条指令依赖于前一条指令的结果,处置处罚器就无法并行实行这些利用,由于第二条指令须要比及第一条指令完成后才气实行。别的,处置处罚器还须要思量是否可以使用多个加法单元同时处置处罚差别的加法利用,并选择空闲的运算单元来实行这些利用。
这齐备都须要大量的电路和复杂的调理逻辑,因此解码指令流并决定怎样实行每一条指令黑白常昂贵的过程。为了优化这一过程,今世处置处罚器通常会使用多个加法单元,而且可以或许并行实行多个加法利用。然而,只管这些优化步伐使得处置处罚器可以或许更高效地执使用命,但它们仍然须要精密的和谐和管理。
如果可以或许使用一次利用处置处罚多个数据,这将大大淘汰处置处罚器的工作量。比方,在处置处罚器中,一次性加载四个数据比逐个加载四个数据要更有效,由于如允许以淘汰解码指令所需的内存带宽。对于加法利用来说,可以或许一次性加四个数,而不是实行四个单独的加法利用,也能显着进步服从。
这种优化方式不但能淘汰处置处罚器中的指令数目,还能淘汰处置处罚器指令缓存的压力。由于每条指令须要的内存带宽更少,因此指令缓存可以或许更高效地存储和读取指令,淘汰了内存的访问压力,从而提升了团体的处置处罚速率和性能。这种通过批量处置处罚数据来优化处置处罚器性能的方式,可以或许在盘算麋集型使掷中带来显着的效益。
黑板:优化思量

今世盘算机, 无论是CPU照旧GPU,都依照一种类似的工作模式。这个模式的根本流程包罗三个部门:指令、缓存和内存。在优化盘算机性能时,必须思量这三者之间的交互和数据传输。
起首,指令是CPU或GPU实行的利用。通常,这些指令涉及到将数据从内存加载到缓存中,再从缓存中加载到寄存器中举行盘算。盘算完成后,结果会被写回缓存,终极再写回到内存。这个过程是一个循环,数据在差别存储条理之间不绝活动。
在实行过程中,指令是宽的,这意味着一条指令通常同时处置处罚多个数据项。好比,一条指令大概同时加上四个数据项,这就是所谓的"单指令多数据"(SIMD)利用。这种方式能显着进步处置处罚器的服从,由于它可以在同一周期内处置处罚更多的数据。
总的来说,优化盘算时须要关注的关键点是怎样高效地将数据从内存移动到缓存,再到寄存器,举行盘算后再返回。这是盘算机体系中的常见模式,全部的优化工作都是围绕怎样高效实行这个数据活动过程睁开的。
黑板:内存访问资本

缓存是处置处罚器上用于加快数据访问的存储地域,通常缓存有差别的层级,分别为L1、L2、L3等。每个层级的缓存间隔CPU的寄存器越远,访问的资本也越高。寄存器(可以视为L0缓存)是处置处罚器直接利用的内存,访问速率最快。接下来是L1、L2、L3缓存,分别是越来越大的存储区,位于CPU内部或较靠近CPU的位置,访问速率渐渐低沉,L1缓存通常须要约莫16个时钟周期来访问,L2缓存更多,L3缓存再多,而主内存的访问时间大概到达300个时钟周期。
当实行指令(如加法指令)时,访问这些缓存的代价会渐渐增长,特别是当须要从更远的缓存或内存中提取数据时。指令实行的时间,比方加法大概是2个时钟周期,但随着数据泉源的间隔增长,整个过程的代价(延长)也会更高。因此,在举行优化时,须要关注怎样淘汰从远缓存或内存获取数据的延长。
这个模子清晰地展示了处置处罚器访问差别缓存层级的服从,而且对优化体系性能至关紧张。
黑板:周期

CPU的工作是以周期为单元来举行的,每个周期代表处置处罚器可以完成的一次利用。处置处罚器的时钟速率决定了它每秒可以或许处置处罚多少个周期。比方,一个3.2 GHz的处置处罚器每秒钟约莫会履历32亿次周期(3.2×10^9)。该频率意味着每个周期处置处罚器可以实行一个利用。
为了衡量性能,通常以每秒多少帧来盘算,在30帧每秒的情况下,处置处罚器每帧所须要的周期数约莫是1亿零700万个周期。如果在这一时间内,处置处罚器不能完玉成部须要的工作,游戏的帧率就会降落,无法维持30帧每秒的体现。因此,性能优化的一个关键点就是确保每一帧的利用可以在这些周期内完成,包管游戏流畅运行。
黑板:你应该始终知道你有多少个周期可用

在优化时,起首须要关注的一个关键数字是每帧的处置处罚周期数。对于一个单核心的CPU,假设每秒有107百万个周期可用来完成使命。如果目标装备是多核处置处罚器,那么每个核心都有如许的处置处罚本事,因此,若工作可以或许分配到多个核心上,总的周期数将会更多。但在这里,我们起首讨论的是单个CPU核心的性能。
107百万个周期并不算多。以1920x1080的分辨率为例,屏幕上有约200万个像素。如果每个像素的渲染约莫须要50个周期,那么对于每帧图像的渲染,现实使用的周期数并不多,乃至大概不敷以完成复杂的利用。这个有限的周期数意味着在每帧的工作量中,每个使命必须非常精简和高效,制止高出这个限定,否则就会导致帧率降落、画面卡顿等题目。
以是,在举行优化时,必须确保全部利用都能在107百万个周期内完成,否则就会超出CPU的处置处罚本事,影响性能和流畅度。
黑板:并非每个周期都可以使用

在思量每帧的处置处罚周期时,须要意识到,通常不会完全使用107百万个周期。这是由于在运行过程中,还会有一些使命发生,这些使命并不直接由应用控制。比方,调用Windows利用体系来表现帧时,体系须要处置处罚一些其他使命,如读取操控杆状态等。这些使命也会斲丧部门处置处罚周期,因此大概无法完全用在应用步调的工作上。
别的,今世盘算机每每具有多核心处置处罚器。如果用户的盘算机设置精良,背景没有占用过多资源的应用步调(如Adobe Acrobat或Creative Cloud等),那么在多个核心上,大概会有靠近107百万个周期的盘算本事可用。但通常,用于与利用体系接口的核心,不会完全拥有这些周期,大概会由于利用体系本身的处置处罚需求而受到影响。
因此,在思量优化时,须要相识并顺应这些外部因素,明白并非全部的周期都能完全用于应用的工作负载。
黑板:什么是周期?

要明白CPU中的一个周期发生了什么,起紧张相识CPU处置处罚指令的根本过程。处置处罚器通常会有一个指令缓存(I-cache),这个缓存用来存储息争码指令。我们编写的代码在编译后会被转换成呆板指令,而这些呆板指令会被加载到指令缓存中,并进一步解码为微代码(microcode)。微代码指令和我们看到的呆板指令之间并不总是完全逐一对应的。
在一个周期内,处置处罚器起首从指令缓存中获取一批指令,数目大概是几条,也大概是更多,详细取决于处置处罚器的筹划。这些指令偶然会按照次序取出,但今世处置处罚器通常会突破次序,从缓存中出于优化目标按需取指。
一旦取出指令,处置处罚器会将这些指令发出到差别的盘算单元中,比方算术逻辑单元(ALU)或内存单元等。在这个过程中,指令的实行大概是乱序的,今世处置处罚器通常支持乱序实行,这意味着处置处罚器会在等候某些指令完成之前,先实行那些不依赖于其他指令结果的指令。比方,如果有一条指令依赖于前两条指令的结果,处置处罚器就会等候它们完成,然后再实行依赖的指令;而那些不依赖于前面的指令的指令则会被立刻实行。
这类优化本领(乱序实行)使得今世处置处罚器可以或许高效地使用周期,只管淘汰因等候依赖指令而浪费的时间。通过这种方式,处置处罚器可以或许在一个周期内最大限度地实行多个指令,进步处置处罚服从。
黑板:流水线阶段

在处置处罚器的流水线中,指令发出后,会进入各个处置处罚单元举行处置处罚。这个流水线分为多个阶段,每个阶段须要肯定的周期时间才气完成。每个指令在流水线中处置处罚时,会履历多个步调,通常包罗取指、解码、实行等。如果实行的是加法利用(ADD),那么加法利用大概会履历两个阶段。
在第一个周期内,指令会进入一个空闲的算术逻辑单元(ALU),该单元会处置处罚加法利用的第一个阶段。然后,在接下来的周期中,该指令会进入流水线的下一个阶段,继续处置处罚加法利用的第二个阶段。终极,当全部阶段完成时,结果才会被写回。
以是,每一条指令的实行会涉及多个周期,这些周期对应流水线的各个阶段。好比,如果加法利用须要两个周期完成,那么在第一个周期中,指令会进入流水线的第一阶段;在第二个周期中,指令会进入第二阶段,终极在第三个周期完成并产收结果。
总之,每个周期都是流水线中的一个时钟周期,指令在流水线中按次序完成差别的阶段,直到终极盘算结果被写出。
黑板:为什么使用流水线?

流水线的目标是进步处置处罚器的服从,使得差别部门的单元可以或许以更高的速率重复使用。这就像在洗衣和干衣过程中使用分开洗衣机和干衣机的方式。假设有一个洗衣机和干衣机,在第一个阶段,衣物被放入洗衣机。洗衣机完成后,衣物进入干衣机,而洗衣机可以立刻开始处置处罚下一批衣物。如许,在有限的时间内,多个洗衣利用可以同时举行,而不会浪费时间等候。
如果使用一台同时举行洗衣和干衣的装备,固然也能完成同样的使命,但每次都须要一个完备的洗衣加干衣周期,这就导致了服从的低沉。详细来说,使用两个分开的装备,可以在五个阶段内完成两批洗衣工作,而使用一个装备则须要六个阶段。在处置处罚更多使命时,分开装备的上风更为显着,由于它每个阶段都在实行工作,不会有浪费的阶段。
通过流水线模子,处置处罚器可以或许在差别的阶段之间重用硬件部件,充实使用盘算资源。每个阶段的工作可以在处置处罚器的多个单元中并行实行,从而加快处置处罚过程。如许,处置处罚器可以或许更有效地管理资源,淘汰等候时间,进步团体性能。
黑板:延长和吞吐量

在盘算机性能中,紧张关注两个指标:延长(latency)和吞吐量(throughput)。延长是指一条指令从发出到完成的时间,也就是从发出指令到终极结果产生所颠末的时间。而吞吐量则是指在流水线完全工作的情况下,可以或许处置处罚多少条指令,即每单元时间内能完成多少使命。
在一个洗衣机和干衣机的例子中,延长和吞吐量的概念也可以得到体现。比方,如果须要洗涤和干燥一批衣物,所需的时间无论是使用分开的洗衣机和干衣机,照旧组合式洗衣干衣机,延长都是类似的,由于每次洗涤和干燥都须要固定的时间。但如果是举行大量的洗衣,分开的洗衣机和干衣机在流水线方式下可以或许更高效地工作,由于每个阶段的装备可以同时工作,从而进步吞吐量。而组合式洗衣干衣机则在处置处罚大量衣物时服从较低,由于它只能在每次完备周期后处置处罚下一批。
对于盘算机中的指令,吞吐量更加紧张。通过流水线结构,处置处罚器可以在每个周期内并行实行多个使命,因此在举行大规模的数据处置处罚时,吞吐量决定了处置处罚器的性能。而延长通常对单个利用的影响较大,但在流水线化工作模式下,延长的影响相对较小,由于处置处罚器会并行处置处罚多个使命,从而制止等候延长。
通常,在性能优化时,更多关注的是吞吐量,而不是单个使命的延长,由于我们假设代码会在流水线中连续运作,从而制止了延长的积累。对于指令而言,吞吐量更能反映体系的团体处置处罚本事,尤其是在处置处罚大量指令时。
然而,延长在某些情况下仍然紧张,比方在访问缓存时,延长大概会影响到数据的读取速率,这时须要特别关注怎样优化缓存访问以淘汰延长。
黑板:延长会在哪些地方造成题目

在处置处罚器的性能分析中,吞吐量和延长是两个关键指标,特别是在内存访问和数据活动方面。吞吐量通常指的是内存或数据可以或许流经处置处罚器的最大速率,通常以带宽(bandwidth)表现,而延长则指的是从哀求数据到得到数据的时间。
在内存体系中,吞吐量表如今理想情况下,内存数据可以或许以多快的速率通过处置处罚器。带宽决定了处置处罚器与内存之间的最大数据活动速率,这通常是以每秒多少GB来衡量的。内存带脱期定了处置处罚器在单元时间内可以或许处置处罚的数据量,因此它是衡量内存访问速率的一个紧张指标。
延长则是指从哀求数据到数据返回所需的时间。比方,如果发出一次内存加载哀求,且该哀求须要300个周期才气完成,那么在这300个周期内,处置处罚器其他指令将不得不等候数据的返回。这种延长对于体系性能的影响非常大,由于它大概导致处置处罚器在等候数据时闲置,无法举行其他盘算利用。
通常,内存的延长大概到达数百个周期,乃至在非常快速的内存体系中,延长仍然大概是数十到数百周期。这意味着在高速处置处罚器期间,内存的访问速率成为了性能瓶颈,只管处置处罚器的盘算速率越来越快,内存访问却没有相应的提升。
总结来说,带宽和延长在内存体系中起着至关紧张的作用。带宽决定了数据活动的最大速率,而延长则影响了每个哀求的相应时间。两者共同决定了处置处罚器可以或许有效执使用命的本事,尤其在须要频仍访问内存的应用中,优化带宽和延长是进步性能的关键。
黑板:缓存未掷中

缓存未掷中(cache miss)是性能优化中的一个关键题目。缓存未掷中发生在处置处罚器哀求的数据不在缓存中时,这时必须从主内存中加载数据,导致高延长。相比之下,缓存的数据访问速率要快得多,L1缓存的访问延长大概仅为2个周期,而L2缓存大概是16个周期左右。因此,如果代码可以或许完全在缓存中运行,制止访问主内存,那么就可以或许制止这些高延长的缓存未掷中,显着进步步调的实行速率。
为了优化缓存的使用,今世处置处罚器支持预取(prefetch)指令。预取指令允许步调员告诉处置处罚器,某些数据将在将来的利用中使用,因此盼望提前将这些数据加载到缓存中。通过如许提前加载数据,可以在须要时制止等候内存访问,从而淘汰延长。比方,某些利用大概须要300个周期才气访问内存,但如果通过预取指令提前加载所需的数据,处置处罚器在实行其他利用时就能制止这些延长,并在返回时直接从缓存中获取数据。
缓存管理和优化是今世盘算中非常紧张的一部门。通过公道使用缓存,尽大概多地在缓存中完成利用,而且优化何时将数据添补到缓存和何时革新缓存,可以淘汰缓存未掷中,制止高延长。别的,今世处置处罚器通过"乱序实行"(out-of-order execution)等技能来进一步优化性能。通过这种技能,处置处罚器可以在等候某些数据加载的同时,实行其他不依赖于这些数据的利用,最大限度地淘汰空闲周期,从而进步团体处置处罚服从。
黑板:超线程技能

超线程技能(Hyper-Threading)是一种处置处罚器技能,通过在处置处罚器内部保存两个独立的状态(比方状态0和状态1),使得处置处罚器可以或许像拥有两个独立的处置处罚器一样工作。每个状态都保持一套完备的处置处罚器状态,允许处置处罚器同时实行两个线程的使命。固然这种技能看起来像是给处置处罚器增长了两个线程,但现实上它并不是简朴地复制两个处置处罚器的状态,而是在处置处罚器遭遇内存访问延长时,通过切换到另一个状态来继续实行工作。
当处置处罚器在实行某个使命时,如果由于内存延长而无法继续实行,它会切换到另一个状态,检察是否能从中找到可实行的使命。如果第二个状态也由于内存延长而停滞,它会再次切换回状态0。通过这种方式,超线程技能可以或许有效淘汰因内存延长导致的处置处罚器空闲时间。
超线程技能的紧张目标是隐蔽内存访问的延长,由于内存相对于处置处罚器来说要慢得多。这种延长会形成“延长气泡”,即处置处罚器等候内存数据时无法实行其他使命。超线程通过在两个线程之间切换,实验淘汰这些延长气泡的影响,保持处置处罚器的高效运转。
除此之外,今世处置处罚器还依赖于步调员自动发出预取指令、写入缓存等本领来进步缓存的使用服从,以进一步减小内存访问延长的影响。处置处罚器的缓存划一性和高效的缓存管理对于优化步调性能至关紧张,固然这些技能在硬件上有所支持,但终极照旧须要步调员在编程中公道使用这些工具来提升步调的团体性能。
黑板:优化,平台

优化的目标是通过公道筹划指令集,确保代码的指令流可以或许最大化地使用CPU的缓存,同时只管制止过大的指令集占用过多的指令缓存。指令流的筹划须要只管小,以便在CPU的缓存中高效运行,制止因缓存溢出导致性能降落。
为了实现这一目标,须要提前相识步调将使用哪些内存,并在大概的情况下提前将数据添补到缓存中。代码还须要可以或许有效地拆分,使用超线程和多个处置处罚器(如果有的话)来最大化CPU的处置处罚本事。在这种筹划中,每一条指令的实行都应该尽大概填满CPU的流水线,以确保处置处罚器在每个周期内都能高效工作,从而最大限度地使用每个周期,提升代码的实行服从。
优化过程中,有些代码紧张依赖于内存的管理,怎样高效地将内存数据通报到CPU并制止内存访问延长;而有些代码则偏重于指令的管理,关键在于怎样高效地安排指令的实行次序。但大部门情况下,优化须要同时关注内存管理和指令管理。起紧张确保内存访问不会导致处置处罚器停滞,之后则要尽大概淘汰处置处罚器对内存的工作量,以保持高效的运算。
总之,优化的核心目标是让步调在处置处罚数据时最大化地使用缓存和CPU的实行本事,淘汰不须要的内存访问,并确保指令流尽大概高效地运行,以得到更高的性能。
黑板:这就是优化

在讨论优化时,起首会合中在怎样编写高效的代码,使其运行速率尽大概快。然而,尚有一种附加的概念,这并不是这次讨论的重点。接下来的讨论将从优化的根本层面开始,紧张关注怎样通过公道的代码筹划提升步调的实行服从。
黑板:服从

在讨论优化时,性能和服从是两个关键的概念。性能优化是最复杂的,它涉及大量的范畴知识,尤其是处置处罚器的工作原理。性能优化的目标是让特定的算法在处置处罚器上尽大概高效地运行,这通常须要深入明白处置处罚器的架构和复杂的优化技能。
而服从优化则更轻易明白,重点是淘汰不须要的工作。服从意味着通过筹划更好的算法,制止实行不须要的盘算。比方,当处置处罚一个三角形时,直接查抄其边界框内的像素就比查抄整个屏幕的全部像素要高效得多。这是一个典范的服从优化案例,算法本身更高效,实行的工作量更少,从而淘汰了不须要的盘算。
服从优化通常比性能优化更为紧张,由于纵然在性能优化方面做得再好,如果算法本身服从低下,那么优化也不会有太大意义。在举行性能优化之前,确保算法的服从已经公道是至关紧张的。
总的来说,服从优化关注的是做最少的工作以得到所需结果,而性能优化则是通过公道的数据结构和利用安排,确保数据能在处置处罚器上以最少的周期移动。通常,性能优化的目标是尽大概到达最佳结果,但并不肯定要求到达极限。大部门情况下,优化目标是到达一个公道的、靠近最佳的状态,而不是寻求完善的性能。
你会乐意做更多的黑板解说吗?这非常有资助

当有须要表明的概念时,会制作更多的黑板教学视频。如果之前的某个话题没有解说够详细,可以提出,之后可以专门做一个黑板课程。每当在游戏开发过程中碰到须要表明的地方,都会制作黑板视频,已往也曾多次在须要的时间做过黑板教学。因此,黑板教学是根据现实须要随时制作的,不会遗漏任何紧张的表明。
你会使用像 VTune 如许的工具来衡量性能吗?

大概不会使用像 vtune 如许的工具来衡量性能。大概会使用自带的时间戳计数器(DTSC)来举行性能丈量。由于 vtune 仍然须要付出高额费用(约莫 $500),而目标是让观众可以或许使用本身能负担得起的工具。以是,筹划写一些自界说的工具来举行丈量,制止依赖昂贵的软件,除非它如今免费提供。
指令是怎样写入缓存内存的?

对于微代码的详细格式,没有确切的相识。这大概是一个很好的题目,可以向英特尔的专家扣问。固然编程了 30 年,但从未见过有人讨论微代码的现实格式,大概这是英特尔的商业机密。大概通过搜索 “Intel microcode format” 或 “x86 microcode format” 可以找到一些干系的信息,大概在英特尔的体系架构手册中有提到。但至今未见干系内容,也没有专门去探求过这个题目标答案。如果它是公开的,大概会有一些文档讨论这个题目。
我们须要手动发出预取指令,照旧CPU会根据我们访问内存的方式自动推测?

预取利用可以是手动的,也可以由 CPU 根据内存访问模式推断出来。在此图示中讨论的是手动预取。早在奔驰处置处罚器期间(大概是 P6 或 P5 版本)就引入了手动预取指令。如果编写的代码频仍发生缓存未掷中的题目,而 CPU 无法正确猜测所需的内存,就可以手动插入预取指令。比方,可以在循环的某部门插入指令,告诉 CPU 提前预取将来频频迭代中须要的数据,如许 CPU 就能更快地获取所需数据。
处置处罚器还提供了非暂时存储等其他指令,实用于办理缓存题目。对于平常优化者而言,这些指令(如预取和非暂时存储)足以办理根本的缓存题目。但对于 hardcore 优化者来说,大概会盼望有更多的控制来优化缓存,如缓存别名(cache aliasing)等复杂题目。
有些平台(比方 PlayStation 3 上的 Cell 处置处罚器)提供了极高的缓存控制本事,步调员可以完全掌控缓存的管理,选择何时、何地移动数据。这种控制力可以或许实现更精致的优化,只管今世 Intel 处置处罚器未必提供云云细粒度的控制。
你怎样估算实现一个特性之前现实工作的量与实现后丈量的量之间的关系?

在实现一个功能之前,通常会举行一些盘算和分析,以相识该功能应该到达的峰值速率,并设定一个目标速率。这包罗估算处置处罚器的周期数、所需的利用数以及大概的优化点。举行这些开端盘算后,可以得出一个大抵的速率预期,然后将现实的性能与这个预期举行对比。如果发现实际速率与预期有较大差距,可以进一步分析大概的缘故因由,并寻求硬件厂商或优化专家的资助。
这种方法有助于明白性能目标,并为后续的优化工作提供参考。通过与预期举行对比,可以辨认出不符合预期的地方,并探究优化方案。对于一些硬核优化职员,他们大概会接洽硬件厂商,获取更多的内部信息来进一步分析和优化性能。
如果内存访问须要几百个周期,而指令须要访问硬盘,这会有什么影响?

随着SSD的遍及,硬盘对性能的影响变得较小,但硬盘访问依然是一个显着的延长泉源。可以将体系的存储条理结构视为一个渐渐增长延长的过程,起始于寄存器,然后是L1、L2、L3缓存,再到内存、硬盘和网络等。随着访问层级的增长,延长也渐渐增大,从几百个周期到成千上万个周期不等。
在处置处罚硬盘访问时,延长变得尤为紧张,因此在涉及到硬盘读取时,通常会接纳重叠利用的方式。比方,在工作中会提前两秒钟乃至更多时间发起硬盘读取哀求,如允许以确保数据在须要时已经准备好,从而制止延长对性能的影响。
两个题目:1) 是否有情况须要担心我们的某条指令被解码为多个微代码指令,而我们无法察觉?

在举行优化时,通常不须要过多关注微指令的细节,由于对于大多数优化工作来说,关注吞吐量和延长的数字就充足了。然而,对于极度优化的情况,确实须要关注微指令的拆分题目。当一条指令被拆解成多个微指令时,大概会对指令的发射产生压力。比方,如果某条指令被拆解成七个微指令,而且这些微指令之间是串行依赖的,那么发射这些指令所需的周期数会增长,大概会导致性能降落。尤其是在没有可以或许隐蔽这些延长的代码时,这种情况会变得尤为严峻。
但这种题目并不常见,通常情况下,不须要过于担心微指令的拆分。如果只是举行一样寻常的优化工作,通常不会碰到这种极限性能的题目,因此不须要思量微指令的细节。
2) 在优化时,你是否设置了代码结构,使得可以按功能逐个优化,照旧我们须要重构一些函数才气举行优化?

在优化时,一样寻常会按函数逐个优化,这是一个很好的做法。编写代码时,应当清晰哪些利用是昂贵的,关键是要思量时间复杂度。比方,循环的嵌套会导致更多的迭代,因此须要关注最耗时的部门。在编写软件渲染器时,像像素添补这种利用通常会占用大量时间,由于它涉及到大范围的循环利用。
在现实编写代码时,通过思量复杂度,可以明白哪些部门是性能瓶颈。比方,在渲染中,像素添补的地域会比力大,而精灵的数目相对较少,这使得添补利用成为最紧张的优化目标。因此,在筹划代码时,应当围绕这些瓶颈举行优化。
别的,固然面向对象编程(OOP)具有肯定的优点,但它的代码结构通常与优化需求不符。通常,写在面向过程的风格中的C语言代码,会更靠近优化后的结构,由于C语言结构体的筹划相对简朴,更得当于优化。而OOP风格的代码每每更复杂,须要更多的重组才气满意优化的需求。因此,选择C风格编程可以资助自然地写出更轻易优化的代码。
将缓存卸载到SSD上,使用最少的RAM,是否会低效,照旧延长会太高?

对于将缓存卸载到SSD的题目,SSD根本上相当于非常慢的内存,位于内存条理结构的较低条理。因此,通常不会直接将数据卸载到SSD,而是先卸载到内存,再将数据写入SSD。这是由于将数据写入SSD时,通常须要与内存利用重叠,以淘汰延长。如果将缓存直接卸载到SSD,延长大概过大,导致服从低下。因此,卸载缓存的次序通常是先到内存,再到SSD。
“过早优化是万恶之源。”你对此有何见解?

“过早优化是万恶之源”这个说法现实上并不完全正确。题目标关键在于“过早”优化毕竟指的是什么。外貌上看,优化太早确实会浪费时间,由于如果游戏已经可以或许以30帧每秒运行,那么在指令级别举行优化就显得毫偶然义,而且大概会使代码更加脆弱和难以维护。但真正的题目在于,有些优化决议并不简朴。好比,是否须要将数据存储在空间条理结构中,这种决议大概对后续的代码架构产生深远影响。延长做出决议,大概会导致后续须要大规模重构代码,这时优化就显得为时已晚。因此,判断优化是否过早黑白常困难的,并不像这句格言所表达的那样简朴。
有办法使用或制止超线程技能以得到上风吗?

可以通过使用超线程和通例线程来优化渲染器的性能,下一次流媒体演示中将讨论怎样在渲染器中使用这两种方式。
你会怎么告诉一个不喜欢 Emacs 的人?

如果有人不喜欢 Emacs,可以发起他们不要使用 Emacs,毕竟每个人都有本身的舒服编辑器。关键是无论使用哪个编辑器,都能到达相当的编程速率。如果不能以符合的速率编程,可以思量进步编辑器的使用熟练度,大概切换到一个更得当的编辑器。终极目标是确保可以或许在编程时保持流畅和高效,如果速率显着慢于盼望,大概须要反思本身对编辑器的把握程度。
超线程是否淘汰最大带宽,由于它必须在状态之间切换,照旧两个状态可以同时利用?

超线程并不会增长带宽,由于它们共享带宽。带宽的界说是处置处罚器可以或许吸收和处置处罚数据的量,因此无论是否使用超线程,带宽的上限是固定的。如果有两个超线程,它们会共享这条带宽,而不会因此得到更高的带宽。以是,超线程并不会进步带宽,反而大概会由于共享带宽而低沉团体性能。
根据你的履历,是什么驱动了“充足好”的优化,初学者怎样把握这一点?

“充足好”的优化原则是相对直接的。游戏须要保持每秒30帧的运行结果,如果某些利用较为泯灭资源,到达30帧/秒的目标就可以以为优化完成了。判断是否告竣目标实在很简朴,就是看游戏是否能在目标平台上顺遂运行,若能稳固在30帧/秒,那就算完成优化。
对于每个详细的利用,衡量其是否靠近最优是通过一些大抵的估算来完成的。一样寻常来说,须要先相识理想情况下处置处罚器能多快完成这个工作,然后与现实的工作速率对比。这种盘算能有效地衡量性能是否优化到位。
至于服从的优化,情况就复杂得多。服从很难正确判断,由于怎样确认一个算法是否高效并不总是显而易见。偶然可以通过已知的理论证实某算法的复杂度是O(n),从而知道算法在理论上是高效的。但偶然现实情况很复杂,比方算法大概借助查找结构来加快,使得在大多数情况下复杂度靠近O(1),但有些情况下大概依然不敷高效,大概还须要调解空间分别等。服从优化经常涉及到大量的细节和反复的迭代。
因此,性能优化每每是一个不绝试错的过程,直到能稳固到达每秒30帧。这个过程没有捷径,必须通过不绝调解和反复测试才气终极找到最佳办理方案。
竣事总结

优化的讨论已经完成,接下来将进入编程部门,筹划尽快开始代码的编写。在这之前,已经通过一场黑板式的解说讨论了优化的干系主题。通过这次解说,盼望各人可以或许明白一些关键概念,好比L3缓存、指令延长、吞吐量等。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表