سرعت و قدرت در سیستم جدید یونیتی DOTS
اگر به سیر تکاملی پردازش در کامیپوترها و بازیهای رایانهای در دههی اخیر نگاهی بیندازیم، متوجه تغییرات اساسی در این ده سال خواهیم شد. اما یکی از بزرگترین تحولات، گذار از دنیایی است که در آن ۹۰ درصد کدها بر روی یک رشته یا یک هسته اجرا میشد به سمت دنیایی که در آن سخت افزارهایی در اختیار همگان قرار دارد که چندین هسته گرافیکی و محاسباتی دارند. در نتیجه در این دنیا ما باید کدهای بهینهای طراحی کنیم که به شکل موازی اجرا شوند و از پتانسیل سختافزار، حداکثر استفاده را ببرند. در نتیجه، یونیتی این ضرورت را احساس کرد که با این الگوی جدید خود را وفق دهد. سیستمهای پایهای Unity در دورهای متفاوت طراحی شدهاند و اکنون زمان آن فرا رسیده است که با آینده سازگار شوند. پشتهی تکنولوژی داده محور (Data-Oriented Technology Stack)که به اختصار DOTS نامیده میشود، نام سیستمی است که برایندِ تلاش تیم توسعه دهندهی یونیتی برای بازسازی معماری داخلیش است. به گونهای که سریعتر، سبکتر و از همه مهمتر به بهینهترین شکل در تعامل با دنیای عظیمِ چند رشتهای کنونی عمل کند.
در این مقاله نگاهی به سه مولفهی اصلی DOTS خواهیم انداخت و این موضوع را مورد بررسی قرار میدهیم که چگونه به کمک آن میتوانیم بازیهای نسل آینده را تولید کنیم.
پشتهی تکنولوژی داده محور (DOTS)
DOTS از سه مولفه تشکیل می شود:
۱.سیستم کامپوننت موجودیت (ECS)
۲.سیستم کاری #C
۳.کامپایلر انفجاری
اکنون میخواهیم نگاهی به این بخشها بیندازیم:
سیستم کامپوننت موجودیت (ECS)
اگر با یونیتی آشنا باشید، میدانید که دو ساختار اساسی که در تمام بخشهای بازی وجود دارند عبارتاند از : GameObject و MonoBehavior . تمامی GameObjectها شامل یک یا چندین MonoBehavior هستند که دادهها (آنچه این شیء میداند) و رفتارهایِ هر عنصر (کاری که این شیء انجام میدهد) در یک Scene را توصیف میکنند.
پیش از هر چیزی بهتر است بدانید که GameObject یک ساختمان داده بسیار سنگین و چاق است! در تئوری، GameObject باید فقط محفظهای برای نگهداری از نمونهیِ MonoBehavior باشد اما در عمل به این شکل پیش نمیرود و مشکلات قابل توجهی وجود دارد، برای مثال:
۱.هر GameObject یک اسم و شناسه دارد.
۲.هر GameObject یک شیء بستهبندی شده #C دارد که به کد نیتیو ++C اشاره میکند.
۳.ساختن و حذف کردن یک GameObject نیازمند قفل کردن و ویرایش یک لیست گلوبال است (و این به این معنا است که این عملیاتها به طور موازی قابل اجرا نیستند).
علاوه بر این موارد، GameObject و MonoBehavior آبجکتهای پویایی هستند و در هر جایی از حافظه ذخیره میشوند. بهتر میبود اگر میتوانستیم تمامی GameObject ها و MonoBehavior ها را نزدیک به همدیگر نگه داریم که در نتیجه یافتن و اجرای آنها بسیار بهینهتر میشد.
برای حل تمامی این مشکلات، یونیتی سیستم کامپوننت موجودیت (ECS) را معرفی و پیشنهاد میکند و آن را به عنوان پارادایمِ جدیدی برای جایگزینی سیستم قدیمیِ GameObject / MonoBehavior مطرح میکند.
همانطور که از اسمِ این سیستم مشخص است، از سه بخش تشکیل شده است:
۱.کامپوننتها: که از لحاظ مفهومی شبیه به MonoBehavior هستند، اما با این تفاوت که فقط شامل داده خواهند بود. برای مثال یک کامپوننت پوزیشن فقط شامل یک وکتور ۳ بعدی خواهد بود، یا یک کامپوننت LinearVelocity فقط اطلاعاتِ سرعت آن شئ را نگه میدارد و به همین ترتیب. کامپوننتها فقط دادههای ساده هستند.
۲.موجودیتها (Entities): آنها فقط مجموعهای از کامپوننتها هستند. برای مثال اگر یک پارتیکل در فضا داشته باشید، میتوانید آن را با لیستی از کامپوننتها نمایش دهید، برای مثال کامپوننتهای Position و LinearVelocity .
۳.سیستم: سیستم جایی است که رفتارها را در آن مدیریت میکنیم. هر سیستم لیستی از کامپوننتها را دریافت میکند و تابعی را روی تمامی موجودیتهایِ ایجاد شده توسط کامپوننتها، به اجرا درمیآورد.
نکته: اگر بخواهیم از لحاظ فنی به شکل صحیحی بیان کنیم، یک موجودیت مجموعهای از ساختمانهای داده نیست. بلکه یک پوینتر است که به مکانی در حافظه اشاره میکند که کامپوننتهایِ موجودیت در آن ذخیره شدهاند. با این حال ذخیرهسازیِ واقعی توسط یونیتی مدیریت میشود.
به کمک این سیستم میتوانیم کامپوننتها را در آرایههای پیوسته ذخیره کنیم و سپس از موجودیت کمک بگیریم، موجودیتی که فقط یک پوینتر به نمونه اولیه است. همچنین یک تابع برای هر سیستم میتواند رفتار هزاران موجودیتِ مشابه را تعیین کند. این رفتار بسیار بهینهتر از اجرا شدن Update بر روی هر MonoBehavior در هر GameObject است.
به همین دلیل در ECS میتوانیم بدون هرگونه کند شدن یا سرباری برای سیستم از موجودیتها استفاده کنیم که در نمونههای GameObject این موضوع غیرممکن بود. برای مثال میتوان تنها از یک موجودیت برای تمام پارتیکلهایِ پارتیکل سیستم استفاده کرد.
سیستم کاری #C
اکنون این موضوع مطرح میشود که ما به ابزاری نیاز داریم که این سیستمها را به شکل بهینهای اجرا کند. همانطور که در مقدمه اشاره شد استفاده کردن از تمامی هستهها به عنوان رویکردِ مدرن در بهینگی مطرح میشود که این روش نیازمند اجرا شدن کدها به شکل موازی با استفاده از سیستمهای عظیم چند رشتهای است.
متاسفانه توسعهی بازی به شکل چند رشتهای کار سختی است، بسیار سخت. هر برنامهنویس با تجربهای میتواند با قطعیت بگوید که گذار از برنامهنویسی تک رشتهای به چند رشتهای مشکلات و باگهای فراوانی را با خود به همراه خواهد داشت، مشکلاتی از قبیل وضعیتهای رقابتی (Race Conditions). علاوه بر این، برای کار به شکل چند هستهای باید تا حد ممکن به سمت برنامه نویسی سطح پایین حرکت کنید و از تخصیص و آزادسازی داینامیک و بازیافت حافظه (GC) که در زبان #C وجود دارد اجتناب کنید و بخشی از بازیتان را در زبان ++C توسعه دهید.
اما خوشبختانه یونیتی مولفهای را درون DOTS تعبیه کرده که هدفش ساده سازی برنامهنویسی چند هستهای در یونیتی از طریق زبان #C است: سیستم کار.
میتوانید کار را به عنوان بخشی از کدها تصور کنید که میخواهید به شکل موازی روی تمام هستههای ممکن اجرا شود. سیستم کار #C به شما کمک میکند تا کدهایتان را به شکلی طراحی کنید تا با استفاده از #C از تمام خطاها و تلههای رایج در مبحث چند رشتهای در امان بمانید. بالاخره میتوانید بدونِ نوشتن یک خط کد ++C، از تمام پتانسیلِ دستگاهتان استفاده کنید.
کامپایلر انفجاری
آیا حرف من را باور میکنید اگر به شما بگویم که این امکان وجود دارد که با نوشتن کد به زبان #C به جای ++C، به راندمان بالاتری دست پیدا کنید؟ آیا فکر میکنید عقلم را از دست دادهام؟ اما هنوز عقلم سر جایش است! و این موضوع ، هدفِ آخرین کامپوننت DOTS است: کامپایلر انفجاری.
کامپایلر انفجاری یک تولید کنندهی تخصصی کد است که یک نوع زیرمجموعه از زبان #C (که High-Performance C# یا به اختصار #HPC نامیده میشود) را به کدهای ماشین کامپایل میکند که اغلب اوقات کوچکتر و سریعتر از کدهای معادلش در ++C است.
کامپایلر انفجاری در حالت آزمایشی است اما شما میتوانید از طریق Package Manager یونیتی آن را به پروژهتان اضافه کنید و مورد بررسی و استفاده قرار دهید. مطمئنا هنگامی که این کامپایلر را با دو مولفهی دیگر DOTS ترکیب کنید، به حداکثر راندمان و قدرت این سیستم دست پیدا خواهید کرد.
نویسنده: محمد علیزاده