思考系列-1:ECS设计思想的本质-面向数据的设计思想

blank

思考系列-1:ECS设计思想的本质-面向数据的设计思想

今天在寻找UE相关文章的时候,无意中找到一篇非常的文章,我觉得不转载一下,对不起作者,而且,作者也欢迎转载。哈哈。。。就帮作者宣传一下呗。

嗯,在开启这篇文章之前,我说一下我自己的感受:

  • 现在记得读大学的时候,”面向对象“这个概念被学校里奉为圭臬,觉得只要是”面向对象“就很NB。其实,在当时,我就依稀有一些疑问,真的”面向对象“就是观看这个世界的终极思想吗?记得当时还去问过授课老师,现在已经记不得授课老师是怎么回复的了?只是大概记得没给我啥好脸色,哈哈。。。可能觉得,你个毛头小子,懂个屁啊?
  • 后来,读研究生的时候,有幸拜入梅宏院士门下,还记得梅老师当时主要是研究Component-based programing。当时,给我们的介绍是说,Component是一个组件的概念,这是一个比对象更高级的功能组件。其实,从后来看,更类似于有一段时间很火的Web Service。当然,你参考现实世界,可以理解成某某服务商提供了一个接口,来让你能实现某某功能;但是,说实话,总感觉是比较虚的。这个东西也只能适用于某一部分的应用吧?
  • 再往后,不知道自己怎么就混到了“游戏行业”(无言。。。)?在研究OpenGL的API和Direct3D的API的时候,感觉怎么针对同一个事情,这两个API差距这么大呢?说实话,即便以我们“面向对象”科班出身的人来看,DirectX把啥都搞成COM组件,也是觉得烦的不要不要的,看人家OpenGL多简单啊。嗯,怎么说呢?我那个时候就得出一个结论:面向对象这套不是万能的
  • 再往后,自己做了一段时间引擎,被OGRE给震撼了,原来面向对象还可以这么用?说实话,曾经有一段时间质疑过自己的结论。再往后,不自己做引擎啥的了,开始接触Unity这个,发现Unity整体是一个面向对象的概念,但是,在面向对象的概念里面,把Component概念有机地结合了进去。说实话,当时不禁拍案叫绝!我总算找到了一个component-based programing的好例子。(当然,这个跟我研究生研究的东西,其实已经完全不是一个东西了。)
  • 最近,Unity又提出了ECS的概念,再我看来,是回到了我们上学时,被批判的体无完肤的面向数据编程。呵呵。。。

怎么说呢?我并不是要吐槽什么,只是感触有点深!一方面是我们的学校教育对什么概念总是会往什么什么革命性改变和终极思想上来教育,另一方面,又是对一个软件开发思想的轮回感慨。

当然,时至今日,我已经形成了自己比较成熟的价值观和世界观:

  • 这个世界上没有最好的技术,只有最合适于你项目的项目;并不是说,把所有的东西都改成ECS,就一定是最好的;比如,Unity中,完全可以将面向对象、Component这个思想,以及ECS充分结合在一起,毕竟他们解决的问题是不同的。
  • 人需要学会深入地思考,尤其是做技术的,而不是被一些技术原教旨主义所左右。针对自己的项目,真正地去思考什么是合适?
  • 尽可能地往下看一下,不要只是停留在对上层技术的细节上。比如,这个ECS为什么有价值?核心的原因是摩尔定律在CPU上的发展速度变慢了,不得不放在多核上。Vulkan为什么会提出多线程渲染的概念?是希望让强大的GPU跑的更好,让CPU的多核性能跑起来。一旦我们真正地理解这些,我们对于技术的把握和展望,会更有信心。

烂七八糟地感慨一番,还是看 房燕良 如何来阐释ECS吧。

(我在整理学习心得的基础上,偶尔也会发一些思考系列,在推荐别人文章的基础上,加上一些我的思考。今天这个算第一篇吧!)

blankblank

【欢迎转载,请注明作者:房燕良,原文出处:游戏程序员的自我修养

来自”玻璃渣”的 Timothy Ford 在 GDC 2017 大会做了一个专题演讲“《守望先锋》架构设计与网络同步(Overwatch Gameplay Architecture and Netcode)”,其中介绍的 ECS 架构一时引起众多关注。在转过年来的 Unity 2018 新版中也支持了 ECS 架构,并且性能有大幅提升!

blankblank

ECS - Entity, Component, System 这一系列概念都是非常简单清晰的,那么为什么 ECS 会成为游戏行业的主流设计思想呢?这还要从”面向数据的设计(Data-Oriented Design)”讲起。ECS 是一种面向数据的设计在咱们游戏、引擎行业中的成功应用,要想理解“WHY”,就必须要理解面向数据的设计这一思想!

面向数据的设计

现代的程序设计思想要求我们以计算机的方式去解决问题(而不是以人脑的方式),“面向数据的设计”思想可以说是沿着这个方向又前进了一大步。这种程序设计的思想的出现和流行和计算机硬件的发展是息息相关的。乔布斯曾经引用过 Alan Kay 的一句名言说软件开发者必须关心硬件。下面我就结合计算机硬件的发展现状来说明一下这种设计思路的优势在哪里,也就是说为什么 ECS 之类是更好的设计!

内存与 CPU 的性能差距

blankblank

在过去几十年 CPU 的工作频率得到了快速提升,而内存(DRAM)的工作频率提升却没有那么快!我作为一个电脑 DIY 玩家是有切身体会的,选购主板、内存、CPU的时候,有两个常见的概念:外频和倍频!外频也就是系统总线工作的频率。例如我家里这台电脑的总线速率是1GHz,CPU 的主频是3.2GHz,倍频为32,而内存主频只有800MHz。CPU通过倍频机制工作在更高的主频之上。从下面这张图,我们可以非常直观的看出内存与 CPU 之间的性能差距。

blankblank

对于 CPU 来说“内存”已经是一个非常缓慢的设备了!为了少被内存拖后腿,CPU 内部集成了越来越多的 Cache。CPU 的内存预读取(prefetch)、多级 Cache 对于我们软件开发者都是透明的,为了验证这个点上的性能差异,我写了一个小小的测试程序。这个测试程序分别以“基于组件的对象设计”和“基于数据的设计”两种方式,对 50000 个 GameObject (或者叫做 Entity) 的 Transform 矩阵进行计算。核心的计算代码都是一样的,最重要的差别就是这些 Transform 数据的内存布局不同。测试的结果十分惊人,在我的电脑上前者要比后者慢 2.8 倍! 这个测试程序的代码请见:github.com/neil3d/GLab/

测试的两者矩阵计算都是一样一样的,它们的差别主要就是内存组织方式,请见下图:

blankblank

由于内存与 CPU 之间的性能差距还在加大,所以将数据以 Cache 友好的方式组织,可以显著提升程序性能。 而且,迄今为止,在这个点上,编译器是帮不上忙的!

多核与并行计算

2005年 Herb Sutter 发表了著名的文章:The Free Lunch Is Over,宣称“免费的午餐结束了”,并行计算是软件开发的下一次变革。所谓“免费的午餐”是指 CPU 主频快速提升的那些年,软件开发者不用花费什么力气就可以得到更高的性能。在 2005 年 CPU 的主频提升已经受到了硅晶片的物理限制,CPU 厂商改走“多核”路线了,作为软件开发者必须掌握“并行计算”的编程技巧才能得到更高的性能。一时间并行编程成了热门,回头看看,OpenMP、Threading Building Blocks 好像都火了一阵子,但最终。。。。至少在3D引擎、游戏开发领域少有人问津。

面向对象在并行编程方面有天生的劣势! 我们通过“封装”把数据放到对象内部去管理,一个对象往往管理了其抽象概念之下的复杂数据。在这种设计之下,这些数据之间的“并行计算”的关系是很难理清的,因为很多东西被刻意“隐藏”起来。我曾经花了很多时间在虚幻引擎的源代码上,虚幻3、4引擎就是一个典型的以面向对象为核心设计思想的多线程架构。现在虚幻4包括主线程、渲染线程、RHI线程、Texture Streamming线程,IO线程、物理线程等等!就拿渲染线程和主线程的交互方式来看,首先你就会发现大量的冗余数据,游戏逻辑层的对象中渲染相关的数据,必须通过 Primitive Proxy 的方式拷贝一份,发送到渲染线程;其次,还是有很多对象是在两个线程上同时要操作的,例如 View 系列的类,想改底层的话,分分钟被锁死!

面向数据的设计,从起点就考虑并行的问题! 在面向数据的设计中,设计的焦点是数据,首先要把数据理清楚,然后把逻辑分离出去。这样做的最大的好处是,每个逻辑模块对数据的读或是写是可以清晰界定的!这样一看,那些计算可以并行,那些是互相依赖的,就一清二楚了!这方面 Unity3D 的 JobSystem 是很成功的!每一个 Job class 可以通过指定 UpdateAfter/Before Attribute 的方式建立依赖关系图,而引擎根据这个图自动调度任务的并行执行!实际上,我认为UpdateAfter/Before 这个名字起的不好,因为本质上你不是在排序 Job 的执行顺序,而是要思考对 ComponentData 的读写!

总结

下面我们就通过对比来回顾一下“面向数据的设计”的核心思想:

blankblank

通过理解“面向数据的设计”,我们就可以对 ECS 架构有一个更清晰的认识:不仅知道“是什么”,还知道了“为什么”。

  • 与“基于组件的对象设计”不同,ECS 架构中的 Component 只包含数据。例如使用 C++ 编程的话,Component就可以是 POD 类型(Plain Old Data)。Entity 也不是面向对象那样把组件、行为封装起来,而是只对应一个 ID。整个这个机制的设计使得所有组件可以在 World 中统一管理的时候,可以使用连续的内存布局,大大提高 CPU 的 Cache 命中率。
  • System 对于组件数据的“读写”是可以明确定义的。从这个数据的读写就可以分析出系统之间的依赖关系,形成一个 DAG。基于这种分析也就可以确定那些系统是可以并行执行的!典型的就是 Unity3D 的 JobSystem。

ECS 只是“面向数据的设计”思想下的一种设计模式,可以预见接下来在各个特定的领域会产生更多的以数据为中心的设计模式,特别是在并行计算领域,面向数据的设计将占主导地位!

What do you think?

Written by marketer

blank

浅谈设计思维

blank

SaaS进入高端市场