زندگی، فلسفه، تکنولوژی صنعت <- اندیشه ها، احساسات

۷ مطلب در شهریور ۱۳۹۶ ثبت شده است

ساخت object pool و خوبی ها و بدی هایش

نوشتن و خواندن در حافظه بسیار کند است و تخصیص آن توسط سیستم عامل و بازپس گیریش از آن هم کندتر است. در زبان های managed مثل جاوا و سی شارپ تخصیص حافظه به برنامه سریعتر است زیرا این کار از حافظه ای که خود framework/runtime برنامه از سیستم عامل گرفته صورت می گیرد. در عوض حافظه تخصیص داده شده باری است بر دوش Garbage Collector. به همین دلیل در تمام برنامه ها و به خصوص در یونیتی که GC چندان خوبی ندارند و از ورژن های قدیمی Mono استفاده می کند، برنامه نویسان سعی می کنند فشار روی GC را (برای جلوگیری از توقف برنامه برای garbage collection) کم کنند.

یکی از راه های کم کردن فشار بر روی سیستم های مربوط به حافظه allocate نکردن حافظه است که در واقع بهترین راه است. چون همیشه این کار ممکن نیست برنامه نویسان سعی می کنند از تکنیکی به نام Pooling استفاده کنند. در این روش تعدادی object از نوع مورد نظر allocate می کنند و هنگام نیاز از آن ها استفاده می کنند. به جز کم کردن میزان allocation ها و قرار دادن آن ها در زمانی مشخص این تکنیک برای object هایی که ساختشان بسیار هزینه بر است مثل database connection ها و ... نیز استفاده می شود. به طور کلی در محیط های managed تکنیک object pooling برای این دو کاربرد یعنی جلوگیری از allocation/deallocation های بی رویه و زیاد و نساختن مجدد object های هزینه بر استفاده می شود. معمولا توصیه می شود که فقط در صورتی این کار را انجام دهید که profiler به شما نشان می دهد که زمان برنامه برای allocation این object ها طلف می شود. این توصیه بیشتر راجع به .NET صدق می کند و هنگام نوشتن بازی mobile در Unity شما طقریبا همیشه نیاز دارید برای اجرای بازی روی گوشی های قدیمیتر با سرعت بالا object هایی که به تعداد زیاد از آن ها استفاده می کنید را pool کنید. وقتی می گویم طقریبا همیشه دارم حرف خطرناکی می زنم زیرا این موضوع بسته به قدیمیترین گوشی که می خواهید پشتیبانی کنید و نوع بازیتان متغیر است. خود بنده بازی های موبایلی را بدون هیچ گونه poolingی منتشر کرده ام ولی در ایران معمولا بازی ها گوشی های قدیمیتری را پشتیبانی می کنند و به همین دلیل در بازی هایی که خیلی خیلی casual و ساده نباشند معمولا به این تکنیک نیاز خواهید داشت.

در روش ساده ای از این تکنیک شما می توانید یک Dictionary<GameObject,List<GameObject>> pool داشته باشید که برای هر prefab به عنوان کلید، لیستی از Object های pool شده را نگه دارد. بهتر است کل pool را درون یک کلاس abstract کنید و هنگام ساخت object ها آن ها را از pool گرفته و SetActive(true) را برایشان صدا بزنید و هنگام از بین بردن یک object برای آن SetActive(false) صدا بزنید و آن را به لیست درون pool برگردانید. برای ساخت pool هایی از object هایی به جز GameObject باید خودتان آن را فعال و غیر فعال کنید. همچنین اگر برای reset شدن object ها نیاز به منطق خاصی است باید ساختاری در component هایتان برای صدا زدن این منطق قبل از ساخت آن و گرفتنش از pool پیاده سازی کنید. مثلا می توانید یک component به نام ResetableComponent داشته باشید با یک متد virtual که آن را بهنگام ساخت، برای object صدا می زنید.

در سرور ها شما معمولا object هایی که خیلی استفاده می شوند و مثلا برای serialization/deserialization داده و یا پاسخ گویی به درخواست ها کاربرد دارند را pool می کنید. به جز آن ها کانکشن های پایگاه داده و کلاینت های HTTP و ... نیز در بیشتر موارد pool می شوند. هنگام ساخت pool در کد های سرور خود باید profiler و لزوم ساخت pool را خوب بررسی کنید زیرا ممکن است که GC بسیار بهتر از شما حافظه را مدیریت کند. هم JVM و هم CLR و ماشین های مجازی خیلی زبان های دیگر بسیاری کارهای پیشرفته را برای مدیریت درست حافظه نسبت به عوامل مختلفی انجام می دهند ، برای همین زیاد برای ساخت pool عجله نکنید. همچنین این کار ممکن است باعث بروز باگ در سیستم شما شود و لزوما کارتان را راحت نمی کند.

pool ها باید چند ویژگی داشته باشند. اول آن که اگر به حد کافی object درونشان نبود بتوانند برای خود چند object اضافه بسازند و ذخیره کنند. دوم آن که باید در ابتدا با تعداد قابل پیکره بندی از object ها initialize شوند. سوم و بسیار مهم آن که پس از استفادهاز pool و تمام شدن کارش، تمام object های خود را برای پاک شدن توسط GC آماده کنند. چهارم که در سرور ها بسیار مهم است آن که هیچ poolی نباید بی نهایت بزرگ شود و شما باید در صورت نیاز نداشتن، object های اضافه ساخته شده در pool را پاک کنید و همچنین باید قابلیت reset کردن pool را هم داشته باشید. مثلا اگر pool شما دارای 64 عدد object است. برای دادن object به دیگر بخش های برنامه object کم می آورید و 32 object جدید می سازید و اندازه poolتان 96 عدد و بعد از مدتی مشاهده می کنید که مدت ها است که بیش از 80 object در pool خود دارید. بهتر است در این حالت بخشی از object های اضافه را پاک کنید و اندازه pool را دوباره به همان 64 تغییر دهید.

۳۰ شهریور ۹۶ ، ۱۷:۴۷ ۰ نظر موافقین ۰ مخالفین ۰
اشکان سعیدی مزده

داستان باگی در بخش شبکه یونیتی در WebGL

از شروع برنامه نویسی به شکل جدی متوجه شدم که معمولا تمام باگ ها متعلق به کد خود من هستند و احتمال وجود باگ در نرم افزار هایی (عموما game engineهایی) که از آن ها استفاده می کنم خیلی خیلی کم است. حتی بحث های زیادی با دوستانم که با من کار می کردند و یا در دانشگاه با آن ها صحبت می کردم راجع به این موضوع داشتم. بعضی بچه ها تا برنامه کار نمی کرد گمان می کردند که مشکل در نرم افزار های مورد استفاده است در حالی که معمولا چنین نیست.

احتمال وجود باگ در کامپایلر ها بسیار کمتر از همه نرم افزار های دیگر، بعد از آن در سیستم عامل ها و بعد در کتابخانه های معروف باگ ها بسیار کم هستند. به هر حال مواقعی هستند که در این نرم افزار ها هم باگ پیدا می شود. شگفت انگیز است که در کامپایلر های معروف پر استفاده ترین زبان ها مثل C هم باگ (هر چند خیلی خاص و کوچک) بعد از سال ها استفاده یافت می شود. اما این موضوع نباید دلیل شود که کسی شک کند کامپایل یک حلقه for ساده با اشکال مواجه باشد. من این مورد را دیده ام و بزرگ نمایی نمی کنم. معمولا باگ ها هنگام تولید feature های جدید و یا در بخش هایی از یک بسته نرم افزاری که چندان مورد استفاده قرار نگرفته یافت می شوند و به سرعت هم برطرف می شوند.

زمستان دو سال پیش من در حال انتقال دو بازی آموزشی به کتابخانه نسبتا تازه شبکه یونیتی که به آن uNet می گویند بودم و قراردادی دو ماهه با شرکت صاحب بازی ها در New York داشتیم. بخش مربوط به webGL کتابخانه شبکه حتی کمتر از بخش اصلی آن تست شده بود و در داخل یونیتی هم کسی خیلی آن را تست نمی کرد. این بخش از WebSocket استفاده می کرد و کاملا از بقیه platform ها که بر پایه UDP بودند متفاوت بود. بازی ما به خوبی اجرا می شد ولی هر چند مسابقه یک بار در وسط بازی همه کلاینت ها disconnect می شدند. ما گمان کردیم که مشکل در کد خودمان است و قبل از چک کردن این موضوع میزان مصرف پهنای باند بازیمان را (که FPS و بسیار سریع بود) تا حد توانمان در زمان محدود پایین آوردیم. ما به این کار به دلایل دیگر هم نیاز داشتیم زیرا WebSocket همواره داده ها را reliable ارسال می کند و برای ساخت بازی FPS خیلی مناسب نیست. مشکل کم شد ولی برطرف نشد. پس از چند هفته بررسی و صحبت با یکی از همکارانم به اسم جکی که از مهندسان اصلی EVE Online بوده و ... گمان کردیم که Nagle Algorithm دلیل مشکل است. مهندسان یونیتی سخت پذیرفتند که آن را disable کنند در حالی که برای بازی ها این کار بسیار معمولی است. پس از بررسی های بیشتر لاگ سرور متوجه شدم یونیتی در سرور errorی لاگ می کند و بعد همه قطع می شوند. error را در گوگل جست و جو کردم که به سایز بافر دریافت داده در کتابخانه WebSocket مورد استفاده یونیتی مربوط می شد. پس از ارسال ناموفق چند پکت داده های ما از 4k بزرگتر می شد و باعث buffer overflow می شد. مهندسان یونیتی امکان تغییر سایز بافر را برای ما ضافه کردند تا ما بتوانیم برای هر کلاینت از بافر بزرگتری استفاده کنیم. سایز این بافر هم نمی تواند خیلی بزرگ باشد زیرا باعث موفقیت آمیز شدن نوع ساده ای از حملات DOS می شود. هر سرور در بازی ما حد اکثر 16 بازی کن را در خود جای می داد و به همین دلیل ما می توانستیم بافر هر کس را به سادگی 12k و یا حتی 16k در نظر بگیریم. اگر قرار باشد یک سرور 20000 کلاینت همزمان را در خود جای دهد، هر 10k بافر اضافه به معنی حدود 195MB حافظه اضافه خواهد بود.

به طور کلی وجود باگ در نرم افزارهایی که ما از آن ها استفاده می کنیم بسیار بدتر از وجود باگ در نرم افزار خودمان است. درست است که زحمت برطرف کردن مشکل گردن کس دیگری می افتد ولی زمان انجام این کار و کیفیتش دست ما نخواهد بود و گاهی این موضوع خیلی حیاطی است. در کیس ما ، بازی باید به مدارس بخش بزرگی از آمریکا فروخته می شد و زمان خرید مدارس تا تاریخ معینی بود و اگر ما بازی را در زمان مناسب به دست مدارس نمی رساندیم ، دیگر آماده شدن بازی فایده ای نداشت. بدی مشکل هم این جا بود که مشکل قطعی فقط وقتی بیش از 5-6 بازی کن از جاهای مختلف و نسبتا دور از هم به سرور وصل می شدند اتفاق می افتاد. اگر local تست می کردیم و یا 5 نفر از یک جا به سرور وصل می شدند معمولا در ارسال پکت ها اختلال کمتر بود و این مشکل پیش نمی آمد. به همین دلیل برای تست کردن یک fix باید چندین نفر از جاهای مختلف کلاینت را اجرا می کردند. راه دیگر داشتن یک ابزار load test نسبتا پیچیده بود که وقت تولید آن را نداشتیم. کل زمان پروژه برای من و برای تبدیل از کتابخانه شبکه uLink به uNet دو ماه بود که به علت وجود این باگ حدود سه ماه طول کشید تا کل پروژه جمع شد. اگر باگ در نرم افزار خودمان بود احتمالا سه هفته از این زمان کم می شد. متقاعد کردن مهندسین یونیتی برای امتحان کردن پروژه ما و یافتن باگ کار سختی بود زیرا نیاز به پروژه ای کوچک بود که مشکل را به سادگی نشان دهد. اگر خودم مشکل آن ها را به شکل دقیق پیدا نمی کردم، معلوم نبود که یکی دو ماه بیشتر طول بکشد یا نه. در ضمن پس از آماده شدن فیکس مربوط، review ها در یونیتی بیش از 2 هفته طول کشید و branch این fix با نسخه اصلی merge نمی شد. مهندسی که با ما کار می کرد سعی کرد با ارسال custom build کار ما را سرعت ببخشد ولی این کار هم دارای مشکلاتی بود و نهایتا ما صبر کردیم تا یک patch release با fix مورد نیاز ما بیرون بیاید. در تمام این مدت آرزو می کردن کاش باگ در کد خودم بود. البته پس از آن هم با مهندس شبکه یونیتی دوست شدم و هم با صاحبان پروژه ای که رویش کار می کردم رابطه ام بسیار بهتر شد و خاطره خوبی برای همه به جا ماند.

۲۵ شهریور ۹۶ ، ۰۰:۱۹ ۰ نظر موافقین ۰ مخالفین ۰
اشکان سعیدی مزده

Garbage Collector چیست و چه انواعی دارد؟

در سال های ابتدایی برنامه نویسی معمولا دانشجو ها با برنامه های کوچک سر و کار دارند. در سال های ابتدایی کار هم معمولا بخش های کوچکی از یک برنامه را می نویسند و یا با زبان های سطح بالا کار می کنند که مدیریت خودکار حافظه دارند. در زبان هایی مثل C/C++ Assembly و ... شما باید خودتان حافظه را مدیریت کنید و اگر به حافظه پویا dynamic memory نیاز داشته باشید باید خودتان آن را از سیستم عامل بگیرید. در زبان هایی که مدیریت حافظه خودکار دارند مثل Java, C#, Python, Go و ... مدیریت حافظه برای شما به طور خودکار صورت می گیرد.

بخشی از runtimeی که برنامه شما را اجرا می کند، مثلا virtual machine مربوط به Java و یا CLR (.NET) به نام Garbage Collector یا همان GC برای شما حافظه را مدیریت می کند. این کار با دو الگوریتم کلی در زبان های تجاری و غیر دانشگاهی اتفاق می افتد. یک نوع Mark and sweep GC (tracing) و نوع دیگر Automatic reference counting GC . بیشتر زبان های معروف از نوع اول استفاده می کنند و زبان هایی مثل Objective-C و Erlang از Automatic reference counting استفاده می کنند. در زبان هایی مثل سی پلاس پلاس و برخی کتابخانه ها و framework های سیستم عامل ها (مثل COM) از reference counting برای مدیریت برخی منابع استفاده می شود.

در روش reference counting شما برای هر assignment و خارج شدن متغیر ازscope یا ست کردن متغیر به null یکی به تعداد reference های یک Object اضافه و یا کم می شود. هر گاه تعداد reference های یک Object صفر شود می توانید آن را از حافظه پاک کنید. این روش باعث می شود هر assignment و خروج متغیر از scope کندتر از یک زبان mark and sweep GC اتفاق بیفتد ولی به طور کلی هر کدام از این دو روش می توانند در شرایط مختلف سریعتر از روش دیگر باشد. در این روش یک فیلد در حافظه خود object تعداد reference ها را می شمارد و پس از صفر شدن destructor را صدا زده و سپس object حافظه خود را به سیستم پس می دهد. در این روش اگر reference حلقه ای بین object ها وجود داشته باشد دیگر هیچ کدام از آن ها از حافظه خارج نمی شوند. به همین دلیل نوع دیگری از reference به نام weak ereference وجود دارد که باعث زیاد شدن تعداد reference های object نمی شود ولی object از وجود آن با خبر است و در پایان عمرش آن را به null ست می کند. این روش به طور کلی کمی سرعت اجرای برنامه را پایین می آورد و در عوض میزان کارایی برنامه کاملا deterministic بوده و هرگز وسط اجرا pause اتفاق نمی افتد و همه thread ها برنامه را اجرا می کنند.

در روش Mark And Sweep یا Tracing محیط اجرای برنامه یا همان runtime هنگام تخصیص حافظه به تمام متغیر های static و متغیر های عمومی و متغیر های قابل دسترسی در گراف برنامه از ریشه نگاه می کند و چک می کند کدام object از لیست object های allocate شده دیگر قابل دسترسی نیست و آن را پاک می کند. این روش می تواند باعث توقف اجرای برنامه یا حد اقل مشغول شدن بعضی از thread ها شود. معموملا این کار در زبان های پیشرفته مثل جاوا و سی شارپ به شکل generational صورت می گیرد که سرعت کار را بالا می برد.

در یک generational GC آبجکت ها ابتدا در gen 0 قرار می گیردند و هنگام اسکن کردن برای یافتن حافظه اضافه GC شما ابتدا gen 0 را بررسی می کند و اگر حافظه لازم را کسب کرد دیگر به اسکن لیست های بزرگتر gen 1 و gen 2 و ... نمی رود که احتمالا object های قدیمی در آن ها هستند که فعلا هم مورد استفاده و لازم هستند. اگر یک object یک بار در gen 0 زنده ماند به gen 1 منتقل می شود و اگر در یک اسکن از gen 1 زنده ماند به gen 2 می رود. این الگوریتم می تواند در محیط های مختلف به شکل های مختلف اجرا شود. در .NET شما دارای 3 generation هستید که با اعداد 0 و 1و 2 مشخص می شوند و object های بزرگتر از 80k هم به Large Object Heap می روند و طقریبا مثل gen 2 با آن ها برخورد می شود.

برای دیدن مقایسه کلی از این دو روش به این لینک مراجعه کنید.
برای آشنایی بیشتر با GC درون دات نت این لینک را ببینید.
در جاوا GC های مختلف با تعداد بالایی نتظیمات وجود دارد که می توانید گوگل را برای یافتن آن ها جست و جو کنید.

برنامه نویسان Unity دقت کنند که این engine از آخرین نسخه GC در Mono استفاده نمی کند و همچنین GC مونو با دات نت فرق دارد و GC موجود در Unity اصلا generational نبوده و لیست همه Object ها را یک جا اسکن می کند و به این دلیل بسیار کندتر می باشد. دلیل این که Unity در حال معرفی Native Collection در C# Job system است خلاص شدن از شر GC است.

۱۶ شهریور ۹۶ ، ۱۵:۳۲ ۰ نظر موافقین ۰ مخالفین ۰
اشکان سعیدی مزده

چرا بازی ساز شدم؟ چرا باید بازی ساز های بیشتری داشته باشیم؟

یک روز خسته از بازی و حرف و درس و ... از مدرسه به خانه آمده بودم که یادم نیست برای نصب چه برنامه ای از همسایه مان یک CD گرفتم که در آن مجموعه ای از برنامه ها نصب بود. CD متعلق بود به یک خدمات کامپیوتری به نام شمیم و درونش برنامه ها با یک فایل readme انگلیسی قرار داشتند. درونش یک برنامه بود به اسم Game Maker 6 که در readmeش نوشته بود، می توانید با این برنامه بازی بسازید. من هنوز خیلی برنامه نویسی بلد نبودم و هنوز انگلیسیم هم چندان تعریفی نداشت اما کنجکاو شدم. من از کودکی همیشه بازی کرده بودم و به بازی خیلی علاقه داشتم. تا آن روز انتخاب هایم از روی نخواستن بود و کمی از روی علاقه. نخواستن، یعنی انسانی نمی خواستم و به ریاضی علاقه داشتم پس به آن رشته فکر کردم. حدود سال سوم راهنایی در شبکه آموزش دیدم فردی پاسکال درس می دهد (حلقه repeat until) و برنامه factorial می نویسد. تا قبل از آن فقط می دانستم برنامه نویسی هست و برایم هیجان انگیز بود ولی آن روز آن درس را یاد گرفتم. من هنوز کد پاسکال و حتی دلفی ننوشته ام و الآن هم چیزی از پاسکال نمی دانم. بعد ها متوجه شدم همسر پسر عمه ام که به تازگی ازدواج کرده بود برنامه نویس است، در سفری یکی دو روزی با او صحبت کردم و به کامپیوتر علاقه پیدا کردم. به هنرزستان رفتم و تا روز یافتن آن CD فقط می دانستم به برنامه های اداری علاقه ندارم و باید برنامه دیگری بنویسم. مثلا نرم افزار مهندسی و یا IDE/compiler ... وقتی کمی با آن برنامه کار کردم فهمیدم به آن علاقه دارم.

چند ماه بعد که بیشتر کلاس زبان رفته بودم، یکی از معلم هایمان به من گفت که اگر آزمایش های درس فیزیک سال های دبیرستان را به شکل interactive پیاده کنم، آن ها را از من خواهد خرید (هر کدام 50000 تومان :) ) با سینا دوستم صحبت کردیم و دوباره به سراغ Game Maker رفتم و این بار با انگلیسی بسیار بهتر سریع خیلی چیزها را یاد گرفتم. آن پروژه سریع کنسل شد اما دیگر دیر بود و ما آلوده شده بودیم. بعد ها با چیز های مختلفی مثل FPS Creator, Dark Basic, 3d game studio و ... آشنا شدیم. با 3d game studio مقادری کار هم در سال اول دانشگاه کردیم و بعد از آن با یونیتی آشنا شدیم و همچنین با فردی که از یک کشور خارجی به ایران آمده بود و می خواست برای ساخت یک بازی یا سایت یا برنامه یا ... روی ما سرمایه گذاری کند. پول زیادی نبود و همه اش خرج خرید لایسنس یونیتی و ساخت مدل شد و ما بدون گرفتن پول سعی کردیم یک MMO کشاورزی برای Facebook بسازیم. چیزی شبیه Harvest Moon ولی به شکل MMO که شکست خوردیم.

این ها اصل مطلب نیست، پس از آن هم کار ما ادامه پیدا کرد و هر یک در جایی به کار های مختلف پرداختیم ولی چرا من بازی ساز شدم؟ چرا به سراغ کارهایی که احتمال پول در آوردنش بیشتر بود نرفتم؟ چرا کاری که همه می کردند را نکردم؟ دلیلش این بود که اولا می خواستم خودم چیزی بسازم و خلق کنم، می خواستم هیجان در کارم باشد و چیزی که می سازم ، اثری باشد که بتوانم به آن عشق بورزم و با جان و دل دوستش داشته باشم. می خواستم خودم بتوانم بفروشمش. این کار قبلا خیلی سختتر بود. وقتی app store رسمی Apple برای iPhone تولید شد، حدودا 20-21 سالم بود. هنگام تولید کافه بازار هم سن پدر بزرگ انوشیروان دادگر بودم. وقتی ما شروع به کار کردیم بازار های ممکن، فروش بازی در ایران بود (که به خاطر وجود بازی گرشاسپ خیلی غیر ممکن به نظر نمی رسید، الآن انتشار بازی فیزیکی در ایران طقریبا غیر ممکن است). فروش در Facebook بود که آن موقع ها سر و صدای زیادی به پا کرده بود. فروش در فروشگاه های خارجی و یافتن پابلیشر و همچنین فروش بازی تبلیغاتی به شرکت ها که ما سعیی هم برای آن کردیم. من اولین بازیم را به شرکت گلرنگ و برای وب سایت آن ها فروختم. بازی برای ایران و با حجم زیر 300 کیلو بایت تولید شده بود و روی web player یونیتی اجرا می شد.

چرا در این شرایط ما به این رویا دل بسته بودیم؟ ما در ابتدا حتی از وجود بازی سازان دیگر خبر نداشتیم. بازی های آقای پویا دادگر را دیدیم ولی ایشان را نمی شناختیم و چیزی که در دبیرستان دیدیم بازی اولشان بود که کیفیتش چیزی بود که ما در نادانی کودکیمان به نظرمان خیلی خوب نیامد و به شدت هیجان زده نشدیم. الآن درک می کنم که چه شجاعت شگفت انگیزی داشته اند و چه مردی بوده اند. در دوره دانشگاه توسط یک هم دانشگاهی با پروژه گرشاسپ آشنا شدیم و با سازندگان بازی که بلاگ هایشان را خواندیم و با ایمیل با آقای فصیحی در ارتباط بودیم (ولی تا 2-3 سال بعد ایشان را ندیدیم). در ذهنمان اصلا چیزی جز بازی سازی نبود. ساخت برنامه اداری و کار برای شرکت های بزرگ برایمان (با ذهنیت ما)اشتباه به نظر می رسید. به میزان سختی کار فکر نمی کردیم. به این فکر می کردیم که چه کاری از بازی سازی می تواند جالبتر باشد. بازی سازی کاری است پر از خلاقیت، دنیایی می سازی که قبل از تو نبوده است و روحت را در آن می ریزی و به گونه ای اثر تو با خودت یکی است. کاری است با مسایل فنی بسیار عمیق و سخت و جذاب و مسایل هنری و حسی فوق العاده. اکنون فقط در باره طراحی بازی (game design) و نوشتن برنامه های لازم صحبت می کنم زیرا هرگز کارهای هنری بازی را به شکل حرفه ای انجام نداده ام. بازی ساختن به شگفتی ساخت نرم افزار خلق دنیایی مجازی را نیز اضافه می کند. نرم افزار ساختن به خودی خود خیلی شگفت انگیز است. ما مثل مهندسین دیگر محدود به قوانین مربوط به فیزیک موادی که با آن ها کار می کنیم نیستیم و فقط محدود به زمان و پیچیدگی برنامه مان هستیم. نرم افزار چیزهای غیر ممکن را ممکن کرده است و اگر به آن خلق دنیا های جدید را بیفزاییم. این حقیقت دارد که با ساخت یک بازی هم می توان نظرات و اندیشه هایمان را در قالبی هنری به دیگران نشان دهیم، هم کسب درآمد کنیم و هم از انجام کاری عمیق و چالش برانگیز لذت ببریم.

دوست دارم دانشجویان بیشتری از دانشگاه های خوب کشور این طور فکر کنند. دوست دارم افرادی که به صنعت می آیند به جای این که فقط به سریعترین راه پول در آوردن از کافه و ساخت بازی های داش مشتی و ... به چیز های دیگر هم فکر کنند. فکر کنند که می خواهند در زندگیشان به چه چیزی برسند. باید چه کار کنند که صنعت در کل دنیا برای آن ها و بازیشان ارزش قایل باشد. ما واقعا می خواستیم یک MMO کشاورزی بسازیم که عاشقش باشیم، ارزش های شخصیمان درونش وجود داشته باشد و بتواند در جشنواره های بین المللی و بین بزرگان صنعتی که آن ها را نمی شناختیم حرفی برای گفتن داشته باشد. ما به صنعت بازی سازی نیامدیم که یک مدل monetization خوب درست کنیم که مردم بیشتر روی تبلیغ ها تپ کنند و بیشتر در بازی خرید کنند و ما در شش ماه پول دار شویم. آمدیم تا زحمت بکشیم و کاری بزرگ انجام دهیم. موفقیت مساله ای بسیار subjective است و من خیلی به دنبال موفقیت به شکل سریع نبوده ام. سعی کردم طبق ارزش هایی جلو بروم مثل ساخت کار با کیفیت، داشتن رویا و انجام کار جدی سالم و تمیز. کاش همه مان هر روز به این موضوع فکر کنیم که چرا کاری را انجام می دهیم و ببینیم اعمال روزانه مان در راستای چرایمان هستند یا خیر. همه می دانیم که شاید روز آخر باشد! اگر بازی نسازیم باید نرم افزار های خشک اداری بسازیم که در آن خلاقیتی نیست و اگر بازی خوب نسازیم باید بازی بی ارزشی بسازیم که بیشتر وقت ها پولی در نمی آورد و به طمع پول ساخته شده است. باید بازی خوب بسازیم که اگر موفقیت مادی آمد، معنویش هم با آن بیاید. باید سخت کار کنیم چون دنیا پر است از آدم های معمولی که به شکل معمولی و عین هم کار و زندگی می کنند و به شکل معمولی برای هر چیزی در زندگیشان وقت می گذارند. معمولی بودن و حوصله نداشتن و سعی نکردن و رویا نداشتن ، دور شدن از انسان بودن است و دور شدن است از ایرانی هایی اصیل در گذشته پر شکوهمان که از آن ها دور شده ایم! به قول John Maxwell افرادی که رویا ندارند مرده اند و فقط هنوز مرگشان را رسمی اعلام نکرده اند. حرف آخر این که از John Carmack پرسیدند که میزان پول و موفقیتی که به دست آورده چه حسی دارد و او به جد گفت که برای فراری نمی توان این همه تلاش کرد و این ها هدف نیستند و بلکه نتیجه کناری کار اصلی و هدف اصلی هستند. من شک ندارم که پول با ساخت کار موفق به دست می آید ولی با کاری که فقط و فقط برای پول ساخته شود معمولا چیز خاصی به دست نمی آید.

۱۵ شهریور ۹۶ ، ۱۲:۰۲ ۰ نظر موافقین ۰ مخالفین ۰
اشکان سعیدی مزده

اهمیت شگفت انگیز فلسفه در تکنولوژی

"اگر به پرتگاه خیره شوید، پرتگاه نیز به شما خیره خواهد شد." "کسی که با هیولاها می جنگد باید مراقب باشد که خود تبدیل به هیولا نشود." فردریک نیچه (ترجمه نادقیق خودم).

سال ها است که به این نتیجه رسیده ام که هنگامی که ما یک تکنولوژی را برای انجام کاری انتخاب می کنیم، فرای از ویژگی های موجود در تکنولوژی و شهرت آن باید در آن بنگریم. تکنولوژی ها شکل فلسفه سازندگانشان را می گیرند و آن را دنبال می کنند. رویکرد سازندگان نه تنها در خود تکنولوژی بلکه در اکوسیستم اطراف آن نیز اثر شگرفی می گذارد.

اگر خوب نگاه کنید، در  C++, Erlang  و ... ویژگی های خاصی را می یابید. سازندگان Erlang به تحمل خطا و Fault Tolerance می اندیشیدند و در نتیجه تمرکز زبان بر روی این موضوع است. CPU از Process ها به شکل خودکار گرفته می شود. سعی بالایی برای کم کردن احتمال Crash در VM شده است و همه کتابخانه های متعلق به Erlang بر روی Scalability و Fault Tolerance متمرکز هستند. در مقابل در سی پلاس پلاس تمرکز بر روی نزدیکی به سخت افزار، وجود کنترل کاملبر روی همه چیز و پرداخت هزینه فقط برای چیزهایی است که استفاده می کنید. همچنین تمرکز بالایی روی کارایی و performance وجود دارد و در نتیجه کتابخانه های بدون allocation و بسیار پیچیده و با کارایی بالا در اطراف آن زیاد یافت می شود.

این موضوع در مورد Unity, Unreal Engine, Nodejs, Orleans, F#, Linux و ... نیز صدق می کند. فقط کافی است سعی کنید از این زاویه به این تکنولوژی ها نگاه کنید تا فلسفه پشت آن ها را در یابید. گاهی هم باید کمی علوم کامپیوتر و تاریخ آن را بشناسید. به این دلیل من فکر می کنم بسیار مهم است فلسفه چیزی که شما می سازید به تکنولوژی مورد استفاده شما نزدیک باشد. این موضوع باعث می شود که چیزی که شما می سازید هم شکل درستی داشته باشد. در IBM و Google تحقیقاتی در رابطه با میزان productivity افراد هنگام کد زدن با زبان های مختلف انجام دادند که نشان داد نوشتن کد در C می تواند به اندازه نوشتن کد در Python سریع باشد.  این موضوع مطلبی جدا از این است که آیا با Python هم شما می توانید برنامه ای به کارایی برنامه C بنویسید و اگر پاسخ مثبت است ، این کار چه قدر زحمت دارد. من فکر می کنم هنگام نوشتن سرور یک بازی RealTime که قرار است بسیار سریع باشد، اگر از تکنولوژی هایی استفاده کنید که با تمرکز بر روی این موضوع ساخته نشده اند و قابلیت های پایه آن ها بر این موضوع متمرکز نیست یا شما فلسفه اشتباهی را دنبال می کنید و متوجه موضوع اصلی مورد تمرکز نیستید و به تحقیق بیشتر نیاز دارید و یا کارتان با تکنولوژی مورد نظر به مشکل خواهد خورد.

مثلا شما می توانید با C کد gameplay بنویسید ، البته برای سادگی بهتر است کامپایلر یک نسخه ساده از C را طراحی کنید. می توانید با NodeJs یک برنامه بسیاری سریع scalable و distributed بنویسید در صورتی که ساختار ها و abstraction های لازم را در یک پلاگین با سی پلاس پلاس پیاده کنید. اما در این صورت شما با C و NodeJs کار نمی کنید. از خیلی از ابزار ها و دانش موجود در جامعه کاربران زبان نمی توانید استفاده کنید و باید برای خود یک سری استاندارد و ساختار و framework مخصوص داشته باشید. فیسبوک کد PHP محدود خود را که به سی پلاس پسال کامپایل می کند به این شکل تولید می کند. نوشتن این کد PHP شبیه همه کد های PHP بیرون فیسبوک نیست. چون فیسبوک شرکتی بسیار بزرگ با برنامه نویس های بسیار خوب است می تواند این کار را راحتتر از بقیه انجام دهد و عملا هم بیشتر کد خود را دیگر با PHP نمی نویسد. در مقابل وقیت شما با Erlang نرم افزار سرور و با سی پلاس پلاس Game Engine می نویسید، احساس می کنید در خانه خود هستید و همه تکنولوژی به سمت درستی جلو می رود و همه به زبان شما صحبت می کنند و همه ابزار ها قابل استفاده هستند و کار شما بسیار با کیفیتتر و بهتر و سریعتر جلو می رود. علاقه مندم نظرات شما را در این باره بدانم.

حتی در نرم افزار هایی مثل سیستم های عامل و رویکرد شرکت ها هم می توانید این موضوع را ببینید. معمولا دید و فلسفه افراد سازنده کاملا شکل دهنده اکوسیستم یک نرم افزار خواهد بود. دلیل توجه فراوان به رابط کاربری و طبیعی بودن همه چیز در OSX و سیستم عامل های قدیمی Apple دید خود استیو جابز و افراد اصلی دیگر در Apple بودند. من شک دارم استیو جابز هرگز می توانست یک برنامه خشک مهندسی با تمرکز روی performance را که قرار است کار خاص خطرناکی را انجام دهد رهبری کند و در مقابل شک ندارم او در توانایی نسل بعد نرم افزار های ارایه محتوی در دوران social network ها و یا شاید سخت افزاری خاص برای کار با آن ها و زندگی در این دوره یکی از بهترین گزینه ها بود. این موضوع در باره افراد دیگر و فلسفه هایشان هم از نظرم صادق است. فکر می کنم ما باید هنگام انتخاب تکنولوژی فلسفه و قابلیت های اصلی تکنولوژی را با چیزی که می خواهیم بسازیم مطابقت دهیم و هرگز از تکنولوژی هایی که برای کارهای متفاوت از نظر ویژگی های اصلی هستند برای کار های نا مربوط استفاده کنیم. وقتی می گویم هرگز، بدین دلیل است که همواره با سیستمی sub-optimal رو به رو خواهیم بود که دلیل ساخته شدنش به روشی خاص آشنایی ما با یک تکنولوژی یا علاقه کورکورانه بوده است.

یک بار ما در MuchDifferent به علت آشنایی اعضای شروع کننده یک پروژه سرور licensing برای تمام محصولاتمان، با PHP و Python ، این سرور را با PHP و یک بار با Python نوشتیم و برای سریع کردن آن به حد نیاز باید کارهای خاصی می کردیم و از کش استفاده می کردیم و ... اما همین سرویس با ASP.NET بر روی Owin توسط افرادی دیگر به سرعت نوشته شد و کاملا جواب گوی تعداد درخواست ها بود و لازم نبود ما برای سریع شدنش کار خاصی نیز انجام دهیم. بسیاری افراد با Django سایت های بسیار بزرگ می نویسند و حتی API می نویسند ولی به طور کلی DJango برای ساخت سایت های forms over data و line of business و ... مناسب است و برای بالا رفتن سرعت باید از cache و تکنولوژی های دیگر استفاده کرد. در مقابل کد دات نت بسیار سریعتر بوده و شما به شکل پیش فرض از یک ORM نه چندان سریع استفاده نمی کنید و راه های زیادی برای سرعت بخشیدن به پاسخ گویی سرور وجود دارد که ساخت یک web service بسیار high performance را خیلی ساده تر می کند. شاید کسی در Python با زحمت فراوان و بدون استفاده از Django بتواند همین کار را بکند، اما آن وقت دیگر از سادگی های Django استفاده نکرده است و لزوما از سادگی های Python برای پروژه های کوچک هم بهره نبرده است. این فلسفه من است و تا به حال از آن سود فراوانی برده ام و دوست دارم نظرات دیگران را نیز در کامنت ها بدانم.

۱۴ شهریور ۹۶ ، ۱۶:۴۶ ۰ نظر موافقین ۰ مخالفین ۰
اشکان سعیدی مزده

Abstraction چیست و کجا از آن استفاده کنیم؟

برای بررسی مفهوم abstraction و information hiding که به پنهان کردن پیچیدگی های بخشی از یک سیستم از بخش های دیگر گفته می شود، و اعتبار آن باید ابتدا نوع نرم افزاری که می نویسیم را مد نظر داشته باشیم. یک مقاله خوب در باره کاربرد مناسب information hiding در بخش خوبی از نرم افزار ها http://www.stevemcconnell.com/ieeesoftware/bp02.htm می باشد. نویسنده مقاله ، همچنین نویسنده کتاب بسیار عالی Code Complete می باشد که دید شخص بنده را به برنماه نویسی تغییر داد و آن را به همه پیشنهاد می کنم.
انواع مختلف نرم افزار عمر های متفاوتی دارند و از ویژگی های متفاوتی نیز برخوردار هستند. برای مثال نرم افزار های شاتل های فضایی عموما بدون هیچ تابعی نوشته می شوند و چند هزار خط کد پشت سر هم هستند (زیر 5 هزار خط). دلیل این کار نیاز به مشخص بودن دقیق تمام کارهایی است که اتفاق می افتد. در این گونه نرم افزار ها این است که هر خواننده ای بتواند بدون رفتن به تابع دیگری یا ... بداند کد دارد دقیقا چه کاری انجام می دهد. بازی ها نوع دیگری از نرم افزار هستند که معمولا عمر نسبتا کوتاه چند ساله دارند و کد آن ها در آینده خیلی مورد استفاده قرار نمی گیرد. به این دلیل خیلی از مسایل مربوط به آماده سازی کد برای تغییرات شدید در آینده در آن ها صورت نمی گیرد. همچنین چون بازی ها در لایه نزدیکتری نسبت به سخت افزار سیستم نوشته می شوند تا performance بهتری داشته باشند، خیلی از کارهایی که در نوشتن نرم افزار های اداری مناسب هستند در آن ها قابل پیاده سازی نیستند. در مقابل برخی نرم افزار های اداری بیش از 50 سال سن دارند و در طول زمان تغییرات فراوانی می کنند.

از نظر من، پنهان کردن پیچیدگی ها پشت توابع جذابترین نوع information hiding می باشد زیرا شما بدون پرداخت هزینه زیادی کد را بسیار خواناتر و بسیار قابل تغییرتر می کنید. در مقابل قرار دادن برخی بخش های برنامه پشت abstraction های مختلف مثل interface ها هیچ سود خاصی ندارد زیرا در صورت تغییر آن بخش نوع رویکرد شما با آن نیز تغییر می کند. تکنولوژی WCF به شدت از این مشکل رنج می برد و برنامه نویس هایی که در تمام توابع به جای برگرداندن یک نوع collection خاص (مثل آرایه یا لیست) یک نوع عمومی (مثل IEnumerable در .NET) را برمی گردانند نیز با این مشکل روبه رو هستند. واقعیت این است که اگر لیست شما به صف یا پشته تبدیل شود ، نوع کارکرد شما و دید شما به آن تفاوت شدیدی خواهد کرد، شما می توانید با یک لیست کتاب در فروشگاه به عنوان یک abstraction برخورد کنید ولی درون این ماژول یا کلاس یا ... باید دقیقا مساله لیست کتاب ها را با توجه به ساختمان های داده مورد استفاده حل کنید. هر برنامه در نهایت یک سری ورودی و یک سری تابع برای تغییر داده ها و ارسال داده ها به خروجی است و به جز این تغییرات هیچ ارزش دیگری ندارد. ما در دنیای واقعی زندگی می کنیم و در حال تلاش برای حل مسایل واقعی روی سخت افزار های واقعی هستیم و همیشه نمی توانیم این مسایل را به مسایلی abstract تبدیل کنیم و خیلی اوقات خیلی از ما این حقیقت را فراموش می کنیم. هنگامی که شما از IEnumerable استفاده می کنید، هنگامی که از Linq استفته می کنید ، تعداد زیادی memory allocation و virtual method call اضافه دارید که می توانستید نداشته باشید. اگر یک وب سایت ساده می نویسید که روزی 300 نفر از آن بازدید می کنند، این مساله اهمیتی ندارد اما اگر در حال ساخت کتابخانه ای برای دیگران هستید، اگر یک نرم افزار high performance یا نرم افزاری برای سرور و یا سخت افزار های کوچک و ضعیف می نویسید باید به این مسایل دقت کنید. من ترجیح می دهم هنگام نوشتن ساده ترین نرم افزار ها هم کار خود را به بهینه ترین شکل ممکن برای حل مساله واقعی پیش رویم انجام دهم تا با مصرف کمتری از منابع ، کار خود را به شکل بهتری انجام داده باشم. اصلا پیشنهاد نمی کنم که برای بهینه کردن برنامه هایی که نیاز ندارند دنبال optimize کردن تمام خطوط باشید بلکه پیشنهاد می کنم به جای نوشتن ابزار و به نوعی compiler و general problem solver سعی کنید مساله پیش روی خود را حل کنید مگر این که دلیلی برای حل مساله ای کلیتر وجود داشته باشد. خیلی وقت ها واقعا شما در حال مساله ای کلی هستید اما خیلی وقت ها این طور نیست. فراموش نکنید خیلی وقت ها تغییرات مورد نظر شما با تغییر یک interface و پیاده سازیش امکان پذیر نیستند، مثلا اگر شما به جای TCP از UDP استفاده کنید و یا به جای SQL Server از Riak به عنوان پایگاه داده استفاده کنید، نمی توانید تمام مسایل مربوط به این تغییرات را پشت یک interface پنهان کنید و خیلی وقت ها رویکرد شما در بخش های مختلف برنامه با این تغییرات نیاز به تغییر خواهد داشت. به این دلیل خوب است اول فکر کنیم که آیا باید برای انجام کاری از abstraction ها استفاده کنیم یا خیر.

نکته آخری که در باره abstraction ها و روش معمول پیاغده سازی آن ها در سیستم های object oriented وجود دارد این است که ساخت سلسله مراتب های بزرگ از object ها ساخت مدلی ذهنی از برنامه را برای دیگران (و خودتان پس از گذشت یکی دو ماه) سخت خواهد کرد و شما با ساختن این سلسله مراتب ها و آماده سازی خود برای آینده ، امروز در حال پرداخت هزینه ای هستید که ممکن است سودش هیچ وقت حاصل نشود. برای درک این موضوع فکر کنید که چند بار این abstraction ها به شما سود رسانده اند و چند بار به دلیل نداشتن توانایی حل مساله شما در ساختار کلی خود، مجبور به تغییر آن ها شده اید. حل مسایل کلی یا خاص و تعریف abstraction ها مسایلی مربوط به هم هستند و معمولا با هم و در کنار هم قرار می گیرند. برخی مسایل بسیار عمومی وجود دارند که از داشتن abstraction بهره می برند و حتی همین abstraction ها هم گاه و بی گاه باید شکسته شودند و شما باید بدانید پشتشان چه خبر است تا بتوانید کارتان را به شکل بهینه انجام دهید. هدفم از نوشتن این پست این بود که تلنگری به ذهن خودم و خوانندگان بزنم که هنگام تولید abstraction ها به هزینه آن ها و میزان فایده آن ها بیشتر فکر کنیم.

۱۴ شهریور ۹۶ ، ۱۱:۴۶ ۰ نظر موافقین ۰ مخالفین ۰
اشکان سعیدی مزده

پست اول و دلیل ساخت بلاگ

11 سال من در دوره دبیرستان پس از آشنایی با نرم افزار Game Maker 6 با بازی سازی آشنا شدم و از آن روز به بعد تمام زندگی حرفه ای و بخش خوبی از زندگی غیر حرفه ایم را در ارتباط با ساخت بازی سپری کرده ام. برای شرکت های مختلف تولید بازی و ابزار های ساخت بازی (میان افزار یا همان Middleware) و ... کار کرده ام و چند بار سعی کرده ام با افراد مختلف و یا به تنهایی شرکتی تاسیس کنم و یا پروژه ای را بسازم. این بلاگ را برای به اشتراک گذاشتن اندیشه هایم و اندک تجربه ام و چیز های جالبی که با آنها آشنا می شوم، ساختم.
به طور مفصلتر ، امروز تو دفتر یکی از دوستان و همکاران در حال صحبت بودیم که به من پیشنهاد ساخت یک بلاگ شخصی فارسی رو داد. من در گذشته پست در gamasutra و بلاگ شخصی خودم ashkasm.blogspot.com و بلاگ های شرکت هایی که در اون ها مشغول بودم زیاد نوشتم (این بلاگ شخصی انگلیسی زیاد پست ندارد اما تعداد زیادی پست در بلاگ های دیگر نوشته ام) اما به علت وجود نداشتن جامعه بزرگ بازی سازی در کشور و مشغولیت های مختلف پز از پایان سال های اول دانشگاه و پاک کردن اول بلاگ خودم در blogfa دیگر بلاگ فارسی ننوشتم.

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

امیدوارم بتوانم با پست هایم هم باعث بهتر فکر کردن خودم و یاد گرفتنم از نظرات دیگران شوم و هم اثر مثبتی نیز بر جامعه بازی سازی ایران عزیزمان داشته باشم.

۱۳ شهریور ۹۶ ، ۲۱:۰۴ ۳ نظر موافقین ۰ مخالفین ۰
اشکان سعیدی مزده