محمد علیزاده
محمد علیزاده
خواندن ۸ دقیقه·۴ سال پیش

سرعت و قدرت در سیستم جدید یونیتی DOTS

اگر به سیر تکاملی پردازش در کامیپوتر ها و بازی های رایانه ای در دهه ی اخیر نگاهی بیندازیم، متوجه تغییرات اساسی در این ده سال خواهیم شد. اما یکی از بزرگترین تحولات، گذار از دنیایی است که در آن 90 درصد کدها بر روی یک رشته یا یک هسته اجرا می شد به سمت دنیایی که در آن سخت افزار هایی در اختیار همگان قرار دارد که چندین هسته گرافیکی و محاسباتی دارند. در نتیجه در این دنیا ما باید کدهای بهینه ای طراحی کنیم که به شکل موازی اجرا شوند و از پتانسیل سخت افزار، حداکثر استفاده را ببرند. درنتیجه یونیتی این ضرورت را احساس کرد که با این الگوی جدید خود را وفق دهد. سیستم های پایه ای Unity در دوره ای متفاوت طراحی شده اند و اکنون زمان آن فرا رسیده است که با آینده سازگار شوند. پشته ی تکنولوژی داده محور (Data-Oriented Technology Stack) که به اختصار DOTS نامیده می شود، نام سیستمی است که برایندِ تلاش تیم توسعه دهنده ی یونیتی برای باز سازی معماری داخلی ش است به گونه ای که سریع تر، سبک تر و از همه مهم تر به بهینه ترین شکل در تعامل با دنیای عظیمِ چند رشته ای کنونی عمل کند.

در این مقاله نگاهی به سه مولفه ی اصلی DOTS خواهیم انداخت و این موضوع را مورد بررسی قرار میدهیم که چگونه به کمک آن می توانیم بازی های نسل آینده را تولید کنیم.

پشته ی تکنولوژی داده محور (DOTS)

سه عنصر اصلی DOTS :

  1. سیستم کامپوننت موجودیت (ECS)
  2. سیستم کاری سی شارپ (C# Job System)
  3. کامپایلر انفجاری (Burst Compiler)

اکنون میخواهیم نگاهی به این بخش ها بیندازیم:


1. سیستم کامپوننت موجودیت (ECS)

اگر با یونیتی آشنا باشید، میدانید که دو ساختار اساسی که در تمام بخش های بازی وجود دارند عبارت است از: GameObject و MonoBehavior . تمامی GameObject ها شامل یک یا چندین MonoBehavior می باشند که داده ها (آنچه این شیء میداند) و رفتار هایِ هر عنصر (کاری که این شیء انجام میدهد) در یک Scene را توصیف میکنند.

پیش از هر چیزی بهتر است بدانید که GameObject یک ساختمان داده بسیار سنگین و چاق است ! در تئوری، GameObject باید فقط محفظه ای برای نگهداری از نمونه یِ MonoBehavior باشد اما در عمل به این شکل پیش نمیرود و مشکلات قابل توجهی وجود دارد، برای مثال:

  1. هر GameObject یک اسم و شناسه دارد.
  2. هر GameObject یک شیء بسته بندی شده #C دارد که به کد نیتیو ++C اشاره میکند.
  3. ساختن و حذف کردن یک GameObject نیازمند قفل کردن و ویرایش یک لیست گلوبال است (و این به این معنا است که این عملیات ها به طور موازی قابل اجرا نیستند).

علاوه بر این موارد، GameObject و MonoBehavior آبجکت های پویایی هستند و در هر جایی از حافظه ذخیره می شوند. بهتر می بود اگر میتوانستیم تمامی GameObject ها و MonoBehavior ها را نزدیک به همدیگر نگه داریم که در نتیجه یافتن و اجرای آنها بسیار بهینه تر میشد.

برای حل تمامی این مشکلات، یونیتی سیستم کامپوننت موجودیت (ECS) را پیشنهاد و معرفی میکند و آن را به عنوان پارادایمِ جدیدی برای جایگزینی سیستم قدیمیِ GameObject / MonoBehavior مطرح میکند.

همانطور که از اسمِ این سیستم مشخص است، از سه بخش تشکیل شده است:

  1. کامپوننت ها: که از لحاظ مفهومی شبیه به MonoBehavior هستند، اما با این تفاوت که فقط شامل داده خواهند بود. برای مثال یک کامپوننت پوزیشن فقط شامل یک وکتور 3 بعدی خواهد بود، یا یک کامپوننت LinearVelocity فقط اطلاعاتِ سرعت آن شئ را نگه میدارد و به همین ترتیب. کامپوننت ها فقط داده های ساده هستند.
  2. موجودیت ها (Entities): آنها فقط مجموعه ای از کامپوننت ها هستند. برای مثال اگر یک پارتیکل در فضا داشته باشید، میتوانید آن را با لیستی از کامپوننت ها نمایش دهید، برای مثال کامپوننت های Position و LinearVelocity .
  3. سیستم: سیستم جایی است که رفتارها رو در آن مدیریت میکنیم. هر سیستم لیستی از کامپوننت ها را دریافت میکند و تابعی را روی تمامی موجودیت هایِ ایجاد شده توسط کامپوننت ها، اجرا میکند.
نکته: اگر بخواهیم از لحاظ فنی به شکل صحیحی بیان کنیم، یک موجودیت مجموعه ای از ساختمان های داده نیست. بلکه یک پوینتر است که به مکانی در حافظه اشاره میکند که کامپوننت هایِ موجودیت در آن ذخیره شده اند. با این حال ذخیره سازیِ واقعی توسط یونیتی مدیریت می شود.

به کمک این سیستم میتوانیم کامپوننت ها را در آرایه های پیوسته ذخیره کنیم و سپس از موجودیت کمک بگیریم، موجودیتی که فقط یک پوینتر به نمونه اولیه است. همچنین یک تابع برای هر سیستم میتواند رفتار هزاران موجودیتِ مشابه را تعیین کند. این رفتار بسیار بهینه تر از اجرا شدن Update بر روی هر MonoBehavior در هر GameObject است.

به همین دلیل در ECS میتوانیم بدون هرگونه کند شدن یا سرباری برای سیستم از موجودیت ها استفاده کنیم که در نمونه های GameObject این موضوع غیرممکن بود. برای مثال میتوان تنها از یک موجودیت برای تمام پارتیکل هایِ پارتیکل سیستم استفاده کرد.


2. سیستم کاری سی شارپ (C# Job System)

اکنون این موضوع مطرح می شود که ما به ابزاری نیاز داریم که این سیستم ها را به شکل بهینه ای اجرا کند. همانطور که در مقدمه اشاره شد استفاده کردن از تمامی هسته ها به عنوان رویکردِ مدرن در بهینگی مطرح می شود که این روش نیازمند اجرا شدن کدها به شکل موازی با استفاده از سیستم های عظیم چند رشته ای میباشد.

متاسفانه توسعه ی بازی به شکل چند رشته ای کار سختی است، بسیار سخت. هر برنامه نویس با تجربه ای می تواند با قطعیت بگوید که گذار از برنامه نویسی تک رشته ای به چند رشته ای مشکلات و باگ های فراوانی را با خود به همراه خواهد داشت، مشکلاتی از قبیل وضعیت های رقابتی (Race Conditions) . علاوه بر این، برای کار به شکل چند هسته ای باید تا حد ممکن به سمت برنامه نویسی سطح پایین حرکت کنید و از تخصیص و آزاد سازی داینامیک و بازیافت حافظه (GC) که در زبان #C وجود دارد اجتناب کنید و بخشی از بازی تان را در زبان ++C توسعه دهید.

اما خوشبختانه یونیتی مولفه ای را درون DOTS تعبیه کرده که هدفش ساده سازی برنامه نویسی چند هسته ای در یونیتی از طریق زبان #C است: سیستم کار.

میتوانید کار را به عنوان بخشی از کدها تصور کنید که میخواهید به شکل موازی روی تمام هسته های ممکن اجرا شود. سیستم کار #C به شما کمک میکند تا کد هایتان را به شکلی طراحی کنید تا با استفاده از #C از تمام خطاها و تله های رایج در مبحث چند رشته ای در امان بمانید. بالاخره می توانید بدونِ نوشتن یک خط کد ++C، از تمام پتانسیلِ دستگاه تان استفاده کنید.


3. کامپایلر انفجاری (Burst Compiler)

آیا حرف من را باور میکنید اگر به شما بگویم که این امکان وجود دارد که با نوشتن کد به زبان #C به جای ++C، به راندمان بالاتری دست پیدا کنید؟ آیا فکر میکنید عقلم را از دست داده ام؟ اما هنوز عقلم سر جایش است ! و این موضوع ، هدفِ آخرین کامپوننت DOTS است: کامپایلر انفجاری.

کامپایلر انفجاری یک تولید کننده ی تخصصی کد است که یک نوع زیرمجموعه از زبان #C (که High-Performance C# یا به اختصار #HPC نامیده می شود) را به کد های ماشین کامپایل میکند که اغلب اوقات کوچک تر و سریع تر از کدهای معادلش در ++C است.

کامپایلر انفجاری در حالت آزمایشی است اما شما میتوانید از طریق Package Manager یونیتی آن را به پروژه تان اضافه کنید و مورد بررسی و استفاده قرار دهید. مطمئنا هنگامی که این کامپایلر را با دو مولفه ی دیگر DOTS ترکیب کنید، به حداکثر راندمان و قدرت این سیستم دست پیدا خواهید کرد.



منبع: Packt

ترجمه شده در شماره 30 بازینامه

برنامه نویسییونیتیبهینه سازیبازی سازی
بازی ساز مستقل و برنامه نویس در TeamLuckyDice. وب سایت شخصی : www.mhalizadeh.com
شاید از این پست‌ها خوشتان بیاید