日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity DOTS 介绍

發(fā)布時間:2023/12/8 编程问答 62 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity DOTS 介绍 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

簡介

簡單介紹 Data-Oriented Technology Stack (DOTS, 數(shù)據(jù)導(dǎo)向型技術(shù)棧) ,其包含了 C# Job System、the Entity Component System (ECS) 和 Burst。

特點

DOTS 要實現(xiàn)的特點有:

  • 性能的準確性。我們希望的效果是:如果循環(huán)因為某些原因無法向量化,它應(yīng)該會出現(xiàn)編譯器錯誤,而不是使代碼運行速度慢8倍,并得到正確結(jié)果,完全不報錯。
  • 跨平臺架構(gòu)特性。我們編寫的輸入代碼無論是面向 iOS 系統(tǒng)還是 Xbox,都應(yīng)該是相同的。
  • 我們應(yīng)該有不錯的迭代循環(huán)。在修改代碼時,可以輕松查看為所有架構(gòu)生成的機器代碼。機器代碼“查看器”應(yīng)該很好地說明或解釋所有機器指令的行為。
  • 安全性。大多數(shù)游戲開發(fā)者不把安全性放在很高的優(yōu)先級,但我們認為,解決 Unity 出現(xiàn)內(nèi)存損壞問題是關(guān)鍵特性之一。在運行代碼時應(yīng)該有一個特別模式,如果讀取或?qū)懭氲絻?nèi)存界限外或取消引用 Null 時,它能夠提供我們明確的錯誤信息。

其中向量化指的是 Vectorization。

向量化的相關(guān)介紹:

  • https://stackoverflow.com/questions/1422149/what-is-vectorization
  • https://www.wikiwand.com/en/Array_programming

Burst

Unity 構(gòu)建了名為 Burst 的代碼生成器和編譯器。

當使用 C# 時,我們對整個流程有完整的控制,包括從源代碼編譯到機器代碼生成,如果有我們不想要的部分,我們會找到并修復(fù)它。我們會逐漸把 C++ 語言的性能敏感代碼移植為 HPC# (高性能 C#,下文會提到)代碼,這樣會更容易得到想要的性能,更難出現(xiàn) Bug,更容易進行處理。

如果 Asset Store 資源插件的開發(fā)者在資源中使用 HPC# 代碼,資源插件在運行時代碼會運行得更快。除此之外,高級用戶也會通過使用 HPC# 編寫出自定義高性能代碼而受益。

ECS Track: Deep Dive into the Burst Compiler - Unite LA

Burst 對于 HPC# 更詳細的支持可以在下面找到:

Burst User Guide

深入棧

向量化(Vectorization)無法進行的常見情況是,編譯器無法確保二個指針不指向相同的內(nèi)存,即混淆情況(Alias)。Alias 的問題在 Unity GDC 中也有一個演講提到過:Unity at GDC - C# to Machine Code。

Collections 類就是為了解決這個問題而誕生的,里面包含 NativeList<T>、NativeHashMap<TKey, TValue>、NativeMultiHashMap<TKey, TValue> 和 NativeQueue<T> 四種額外的數(shù)據(jù)結(jié)構(gòu)。

兩個 NativeArray 之間從不會發(fā)生混淆這種情況,這也是為什么我們將會經(jīng)常使用這些數(shù)據(jù)結(jié)構(gòu)。我們可以在 Burst 中運用這個知識,使它不會由于害怕兩個數(shù)組指針指向相同內(nèi)存而放棄優(yōu)化。

Unity 還編寫了?Unity.Mathemetics?數(shù)學庫,提供了很多像 Shader 代碼的數(shù)據(jù)結(jié)構(gòu)。Burst 也能和這數(shù)學庫很好的工作,未來 Burst 將能夠為?math.sin()?等計算作出犧牲精度的優(yōu)化。

對于 Burst 而言,math.sin()?不僅是要編譯的 C# 方法,Burst 還能理解出?sin()?的三角函數(shù)屬性,同時知道 x 值較小時會出現(xiàn)?sin(x)?等于 x 的情況,并了解它能替換為泰勒級數(shù)展開,以便犧牲特定精度。

跨平臺和架構(gòu)的浮點準確性是 Burst 未來的目標。

傳統(tǒng)模式的問題

傳統(tǒng)模式指的是什么呢?

  • 跟 MonoBehaviours 打交道
  • 數(shù)據(jù)和其處理過程耦合在一起
  • 高度依賴引用類型

問題一:數(shù)據(jù)分布在內(nèi)存的各個角落

離散的數(shù)據(jù)導(dǎo)致搜索效率十分低下,還有 Cache Miss 的問題,這個問題可以參考下面的鏈接:

ECS的泛泛之談

問題二:很多不必要的數(shù)據(jù)也被提供了

例如當我們要調(diào)用 Transform 時,可能實際上我們只需要 position 和 rotation 兩個屬性來移動 gameObject,但是其他不需要的數(shù)據(jù)也被提供給了 gameObject。

問題三:低效的單線程數(shù)據(jù)處理

傳統(tǒng)模式只使用單線程來按順序一個一個地處理數(shù)據(jù)和操作,這樣十分低效。

高性能 C#(HPC#)

當我們使用 C# 語言時,仍然無法控制數(shù)據(jù)在內(nèi)存中如何進行分布,但這是我們提升性能的關(guān)鍵點。

除此之外,標準庫面向的是“堆上的對象”和“具有其它對象指針引用的對象”。

也就是意味著,當處理性能敏感代碼時,我們可以放棄使用大部分標準庫,例如:Linq、StringFormatter、List、Dictionary。禁止內(nèi)存分配,即不使用類,只使用結(jié)構(gòu)、映射、垃圾回收器和虛擬調(diào)用,并添加可使用的部分新容器,例如:NativeArray 和其他集合類型。

我們可以在越界訪問時得到錯誤和錯誤信息,以及使用 C++ 代碼時的調(diào)試器支持和編譯速度。我們通常把該子集稱為高性能 C# 或 HPC#。

它可以被總結(jié)為:

  • 大部分的原始類型(float、int、uint、short、bool...),enums,structs 和其他類型的指針
  • 集合:用?NavtiveArray<T>?代替?T[]
  • 所有的控制流語句(除了 try、finally、foreach、using)
  • 對?throw new XXXException(...)?給予基礎(chǔ)支持

Job System

Job System 是針對上述傳統(tǒng)模式問題的一種解決方式。例如下圖可以把發(fā)射子彈看成一個 Job,從而用多線程來并行地處理發(fā)射操作。

目前主流的 CPU 有 4-6 個物理核心,8-12 個邏輯核心,多線程處理將能夠更好地發(fā)揮 CPU 的性能。

傳統(tǒng)的多線程問題也有很多:

  • 線程安全的代碼十分難寫
  • 競態(tài)條件,也就是計算結(jié)果依賴于兩個或更多進程被調(diào)度的順序
  • 低效的上下文切換,切換線程的時候十分耗時

而 Job System 就是專注解決上面問題的一個方案,這樣我們就能享受著多線程的好處來開發(fā)游戲。當然了,我們也要寫出正確的 ECS 代碼,熟悉新的開發(fā)模式。

解決的多線程問題

C++ 和 C# 都無法為開發(fā)者編寫線程安全代碼提供太多幫助。即使在今天,擁有多個核心游戲消費級硬件發(fā)展至今已經(jīng)過去了十年,但依舊很難有效處理使用多個核心的程序。

數(shù)據(jù)沖突,不確定性和死鎖是使多線程代碼難以編寫的挑戰(zhàn)。Unity 想要的特性是“確保代碼調(diào)用的函數(shù)和所有內(nèi)容不會在全局狀態(tài)下讀取或?qū)懭搿薄nity 希望應(yīng)該讓編譯器拋出錯誤來提醒,而不是屬于“程序員應(yīng)遵守的準則”,Burst 則會提供編譯器錯誤。

Unity 鼓勵 Unity 用戶編寫 “Jobified” 代碼:將「所有需要發(fā)生的數(shù)據(jù)轉(zhuǎn)換」劃分為 Job。

Job 會明確指定使用的只讀緩沖區(qū)和讀寫緩沖區(qū),嘗試訪問其它數(shù)據(jù)會得到編譯器錯誤。Job 調(diào)度程序會確保在 Job 運行時,任何程序都不會寫入只讀緩沖區(qū)。Unity 也會確保在 Job 運行時,任何程序都不會讀取讀寫緩沖區(qū)。

如果調(diào)度的 Job 違反了這些規(guī)則,我們會得到運行時錯誤(通常這種錯誤會在競態(tài)條件出現(xiàn)時得到)。錯誤信息會說明,你正在嘗試調(diào)度的 Job 想要讀取緩沖區(qū) A,但你之前已經(jīng)調(diào)度了會寫入緩沖區(qū) A 的 Job ,所以如果想要執(zhí)行該操作,需要把之前的 Job 指定為依賴。

Entity Component System

Unity 一直以組件的概念為中心,例如:我們可以添加 Rigidbody 組件到游戲?qū)ο笊?#xff0c;使對象能夠向下掉落。我們也可以添加 Light 組件到游戲?qū)ο笊?#xff0c;使它可以發(fā)射光線。我們添加 AudioEmitter 組件,可以使游戲?qū)ο蟀l(fā)出聲音。

我們實現(xiàn)組件系統(tǒng)的方法并沒有很好地演變。過去我們使用面向?qū)ο蟮乃季S編寫組件系統(tǒng),導(dǎo)致組件和游戲?qū)ο蠖际恰按罅渴褂?C++ 代碼”的對象,創(chuàng)建或銷毀它們需要使用互斥鎖修改“id 到對象指針”的全局列表。

通過使用面向數(shù)據(jù)的思維方式,我們可以更好地處理這種情況。我們可以保留用戶眼中的優(yōu)良特性,即只需添加組件就可以實現(xiàn)功能,而同時通過新組件系統(tǒng)取得出色的性能和并行效果。

這個全新的組件系統(tǒng)就是實體組件系統(tǒng) ECS。簡單來說,如今我們對游戲?qū)ο筮M行的操作可用于處理新系統(tǒng)的實體,組件仍稱作組件。那么區(qū)別是什么?區(qū)別在于數(shù)據(jù)布局。

ECS 數(shù)據(jù)布局

ECS 使用的數(shù)據(jù)布局會把這些情況看作一種非常常見的模式,并優(yōu)化內(nèi)存布局,使類似操作更加快捷。

原型(Archetype)

ECS 會在內(nèi)存中對帶有相同組件(Component)集的所有實體(Entity)進行組合。ECS 把這類組件集稱為原型(Archetype)。

下圖的原型就是由 Position 組件、Velocity 組件、Rigidbody 組件和 Renderer 組件組成的。

如果一個實體只有三個組件(不同于前面提到的原型),那么那三個組件就組成了一個新的原型。

下面的圖來自 Unite LA 的一次演講的講義, 很遺憾那次演講沒有錄制下來。講義可以在這里找到。

ECS 以 16k 大小的塊(Chunk)來分配內(nèi)存,每個塊僅包含單個原型中所有實體組件數(shù)據(jù)。

一個帖子中有人提供了更加形象的內(nèi)存布局圖,例如上半部分的原型由 Position 組件和 Rock 組件組成,其中整個原型占了一個塊(Chunk),兩個組件的數(shù)據(jù)分別存在兩個數(shù)組中,里面還帶著組件數(shù)據(jù)對應(yīng)的實體的信息。

每個原型都有一個 Chunks 塊列表,用來保存原型的實體。我們會循環(huán)所有塊,并在每個塊中,對緊湊的內(nèi)存進行線性循環(huán)處理,以讀取或?qū)懭虢M件數(shù)據(jù)。該線性循環(huán)會對每個實體運行相同的代碼,同時為 Burst 創(chuàng)造向量化(Vectorization,可以參考?StackOverflow 的問題)處理的機會。

每個塊會被安排好內(nèi)存中的位置,以便于快速從內(nèi)存得到想要的數(shù)據(jù),詳情可以參考下面的文章。

Unity2018 ECS框架Entities源碼解析(二)組件與Chunk的內(nèi)存布局 - 大鵬的專欄 - CSDN博客

實體(Entity)

實體是什么?實體只是一個 32 位的整數(shù) key (和一些額外的數(shù)據(jù)例如 index 和 version 實體版本,不過在這里不重要),所以除了實體的組件數(shù)據(jù)外,不必為實體保存或分配太多內(nèi)存。實體可以實現(xiàn)游戲?qū)ο蟮乃泄δ?#xff0c;甚至更多功能,因為實體非常輕量。

實體的性能消耗很低,所以我們可以把實體用在不適合游戲?qū)ο蟮那闆r,例如:為粒子系統(tǒng)內(nèi)的每個單獨粒子使用一個實體。

實體本身不是對象,也不是一個容器,它的作用是把其組件的數(shù)據(jù)關(guān)聯(lián)到一起。

系統(tǒng)(System)

我們不必使用用戶的 Update 方法搜索組件,然后在運行時對每個實例進行操作,使用 ECS 時我們只需靜態(tài)地聲明:我想對同時附帶 Velocity 組件和 Rigidbody 組件的所有實體進行操作。為了找到所有實體,我們只需找到所有符合特定“組件搜索查詢”的原型即可,而這個過程就是由系統(tǒng)(System)來完成的。

很多情況下,這個過程會分成多個 Job ,使處理 ECS 組件的代碼達到幾乎 100% 的核心利用率。ECS 會完成所有工作,我們只需要提供對每個實體運行的代碼即可。我們也可以手動處理塊迭代過程(IJobChunk)。

當我們從實體添加或移除組件時,ECS會切換原型。我們會把它從當前塊移動到新原型的塊,然后交換之前塊的最后實體來“填補空缺”。

在 ECS 中,我們還要靜態(tài)聲明要對組件數(shù)據(jù)進行什么處理,是 ReadOnly 只讀還是 ReadWrite 讀寫(Job System 一小節(jié)提到過的兩種緩沖區(qū))。通過確定僅對 Position 組件進行讀取,ECS 可以更高效地調(diào)度 Job ,其它需要讀取 Position 組件的 Job 不必進行等待。

大體上,實體提供純粹的數(shù)據(jù)給系統(tǒng),系統(tǒng)根據(jù)自己所需要的組件來獲得相應(yīng)的滿足條件的實體,最后系統(tǒng)再通過多線程來基于 Job System 來處理數(shù)據(jù)。

這種數(shù)據(jù)布局也解決了 Unity 長期以來的困擾,即:加載時間和序列化的性能。現(xiàn)在從大型場景加載或流式處理 ECS 數(shù)據(jù)的時間,不會比從硬盤加載和使用原始字節(jié)多多少。

優(yōu)點

總的來說,ECS 有以下好處:

  • 為性能而生
  • 更容易寫出高度優(yōu)化和可重用的代碼
  • 更能充分利用硬件的性能
  • 原型的數(shù)據(jù)被緊密地排列在內(nèi)存中
  • 享受 Burst 編譯器帶來的魔法

缺點

對 ECS 的常見觀點是:ECS 需要編寫很多代碼。因此,實現(xiàn)想要的功能需要處理很多樣板代碼。現(xiàn)在針對移除多數(shù)樣板代碼需求的大量改進即將推出,這些改進會使開發(fā)者更簡單地表達自己的目的。

Unity 暫時沒有實現(xiàn)太多這類改進,因為 Unity 現(xiàn)在正專注于處理基礎(chǔ)性能。

太多樣板代碼對 ECS 游戲代碼沒有好處,我們不能讓編寫 ECS 代碼比編寫 MonoBehaviour 更麻煩。
——Unity

而為網(wǎng)頁游戲而生的基于 ECS 的 Project Tiny 已經(jīng)實現(xiàn)了部分改進,例如:基于 lambda 函數(shù)的迭代 API。

最后

由于自己空閑時間不多,只能囫圇吞棗地拼湊出這樣一篇筆記。上面大部分文字都是來自 Unity 的博文介紹,自己加了其他的內(nèi)容幫助理解。本文從內(nèi)存布局介紹了 ECS 的概念,也介紹了 Job System 和 Burst。我相信走過一遍文章之后,能清楚 Unity 對數(shù)據(jù)驅(qū)動的未來開發(fā)趨勢的布局,也能更加容易從?Unity ECS Sample?中理解如何實踐 ECS。

總結(jié)

以上是生活随笔為你收集整理的Unity DOTS 介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。