Minutri | Unity

Description
Канал о разработке на Unity, C# и GameDev в целом.

Обо мне: Senior Unity developer
https://t.me/minutri_gamedev/2

Для связи: @MinutriNet
Advertising
We recommend to visit

Официальный канал @CryptoBot.

Возможности бота: @CryptoBotTips

Поддержка: @CryptoSupportBot

Наш чат: @CryptoBotRussian

X: x.com/CryptoBotHQ

Last updated 1 week, 2 days ago

#1 канал о блокчейне, криптовалютах и децентрализованных финансах.

🔥 Реклама — @DCTeam

Last updated 21 hours ago

Канал о TON и все что с ним связано:
1. Аналитика
2. Инсайды
3. Авторское мнение

Ведро для спама: @ton_telegrambot

Бот с курсами криптовалют: @TonometerBot

Чат: @chaTON_ru

Админ: @filimono

Last updated 4 days, 17 hours ago

7 months, 1 week ago

👀🗑Сборка мусора в Unity? Часть 3
<< (Предыдущая часть)

Выделение памяти

Чтобы разобраться со сборкой, нужно понять, а как эта самая память выделяется.
Unity не использует методы распределения памяти напрямую от ОС, такие как malloc и free (для выделения и освобождения каждого объекта).
А использует набор алгоритмов распределения памяти Boehm GC для управления выделением.

Распределение памяти в Boehm GC — объемная тема. Я постараюсь сократить до одного поста, хоть это и трудно.

```
void * GC_malloc(size_t lb)
return GC_malloc_kind(lb, NORMAL);

void * GC_malloc_kind(size_t lb, int k)
return GC_malloc_kind_global(lb, k);
```

Есть два основных метода. GC_malloc выделяет память размера lb. GC_malloc_kind — размером lb и типом k.
k может быть = NORMAL, PTRFREE, UNCOLLECTABLE. В IL2CPP они называется AllocationKind

*⚡️NORMAL — нетипизированное распределение памяти.
*⚡️PTRFREE — выделение памяти, в котором явно сообщается GC, что в объекте нет информации указателя. То есть нет необходимости искать ссылки на другие объекты в объекте во время сборки.
Mono/IL2CPP использует этот тип распределения памяти для массивов целых, байтовых, строковых и так далее.
⚡️UNCOLLECTABLE** — память, которую не нужно помечать или очищать (это например статик корни, поговорим в другом посте)
На самом деле есть еще один тип — kObject, но его можно отдельно затронуть.

Нужно определить большой размер объекта или маленький. Большой, обычно, который больше 2048 байт.

— Если маленький объект:
Мы НЕ выделяем фактический размер. Мы выделяем память кратную 16 байтам для x64 систем (8 для x32), для корректного выравнивания по указателю.
Память дробится на гранулы.

Гранула — минимальная выделяемая единица Boehm GC. Она равна 16(8) байтам.

Таким образом, если нужно выделить память под 18 байт, то мы будем выделять 2 гранулы (32 байта).

Далее происходит расчет и нахождение нужного места в памяти. По связному списку указателей на места в памяти мы находим нужный блок (условно удовлетворяющий нас по объему). Резервируем его.
Если же нужный блок памяти мы не находим — GC смотрит возможность выделения нового участка и проверяет возможность добавления этого участка в свой резрев/список.

— Если большой объект:
Вначале, нужно знать вот о чем.
Базовой единицей каждого блока памяти в Boehm, в связном списке, является блок памяти размером 4096. Блок памяти размером 4096 — называется HBLK. То есть для больших, HBLK >= 1. Опять идет расчет и нахождение нужного места в памяти, но теперь в участки HBLK.

— На самом же деле это объемный процесс, поэтому сократим тем, что описано выше. Знать о реализации выделения в подробностях не обязательно. Но в целом потом можно будет разобрать это не в рамках tg.👀

Мы все ближе подходим к самой сборке)

Кто дочитывает до конца — большое спасибо за проявленный интерес. Задавайте вопросы, я постараюсь на них ответить.

Дальше — интересней👊

7 months, 1 week ago
***❓*** **Логическая задача с собеседования** **#2**

Логическая задача с собеседования #2

Утро доброе! Понедельник начнем с задачки.
Разомнем мозг)))

Альпинист стоит на скале, с обрывом. Пути назад у него нет, только в обрыв.
Высота скалы — 100 метров.
Для альпиниста высота детская, но проблема в том, что у него с собой только веревка длиной 75 метров, а внизу острые камни, так что прыгать — не вариант.

На его счастье на самом краю скалы перед обрывом было прочное дерево, за которое можно заякориться, а внизу прямо под деревом на высоте 50 метров был выступ с пнём. В этот момент альпинисту очень захотелось курить, он полез в карман, но вспомнил, что не курит, а сигарет у него отродясь не было, зато он нащупал складной острый нож и через пару минут уже спускался вниз. Все прошло благополучно, прыгать не пришлось. Вопрос: как спустился альпинист?

Нож нам нужен для того, чтобы разрезать верёвку на две: длиной 50 и 25 метров. Отрезать именно так — несложная задача. Можно сложить верёвку втрое и одну часть отрезать. А можно спустить один конец веревки до выступа (это 50 метров) и сделать разрез на краю скалы (главное — не отпустить веревку вниз, иначе точно никак не спуститься).

Дальше берем короткую веревку (25 метров), один её конец привязываем в стволу дерева, а на другом делаем петлю. В эту петлю продеваем длинную веревку и протаскиваем её в петле до середины. На ней мы спускаемся до выступа, вытягиваем длинную веревку из петли, обвязываем один её конец вокруг небольшого дерева на выступе (или за тяжелый камень) и спускаемся дальше. 50-метровой веревки оказывается как раз достаточно. Ну а короткая веревка с петлей так и остается висеть на дереве.

7 months, 2 weeks ago
7 months, 2 weeks ago

Прочитал интересную статью Егора Бугаенко. Кто не знает, это директор лаборатории в Huawei, топ контрибьюторов GitHub, автор известного труда «Elegant Objects».

В этой статье он рассуждает о двойной инициализации: вначале через конструктор, потом через метод init.
Приводит ссылку на страницу Microsoft, в которой сказано, что двойная инициализация это хорошо (рекламируется как «всегда более безопасный» подход к созданию объектов).
После, выражает свое мнение и подкрепляет аргументами, почему так делать плохо.

В целом, есть свои за и против.

Все пункты в статье важны, но мне показался аргумент с Database наиболее сильным.
Перескажу вкратце:
Есть какой-нибуть dto класс, например database. В нем скажем 10 полей.
В нем мы создаем конструктор (далее ctor) и метод init. Эти 10 полей сначала выставляются дефолтными в ctor, а потом в init приходит config и мы уже инициализируем значения из config.

class Database { private String host; private int port; private String login; private String password; Database() { this.host = "localhost"; this.port = 5432; this.login = "pgsql"; this.password = ""; } void init(Config cfg) { this.host = cfg.getHost(); this.port = cfg.getPort(); this.login = cfg.getLogin(); this.password = cfg.getPassword(); } }

Вроде бы разумно делать init. Но автор подчеркивает, что этот класс в себе содержит слишком много внешних элементов. И проблема серьезная: при расширении кода, ответственность будет расти.

Значит нужно уменьшить ответственность и перейти на сеттеры, заполнять через них грубо говоря избавившись от init. Или заполнять напрямую в ctor.

Как это все перекликается в рамках Unity, что мы должны знать.

  1. Нужно понимать, что автор считает, что DI контейнер «добавляет лишнего шума». Что правильный ООП должен выглядеть так, как приведено в другой статье.

На практике же мы DI контейнеры используем. И используем где-то по своей инициативе, где-то по требованиям команды.

Почему я начал говорить о контейнерах? Дело в том что, используя их, мы встречаемся с методом Init. Для чего? Зачастую разрешение зависимостей должно происходить раньше загрузки данных приложения. Поэтому мы сначала разрешаем зависимости, ждем загрузки данных, и только потом инициализируем классы как раз через условно Init.

Можно ли заменить Init на присваивание через сеттеры? Да. Но нужно ли? Я считаю, что нет.

Факт в другом: нужно правильно проектировать отношения сущностей, исключать множество ответственностей. Тогда метод Init станет кратким и проблем с SRP не будет.

  1. О использовании в Unity без DI контейнеров.
    В таком случае да, можно поступить, как автор статьи. Но не везде. Например — monobeh классы.

Намеренно сократил; Пишите свои мысли в комментариях, дополню статью вашими интересными замечаниями и идеями🤨

Yegor Bugayenko

Is Two-Step Initialization a Solution or a Symptom?

Sometimes you might be tempted to use a two-stage construction for your object. However, I suggest reevaluating your design principles if you feel such an inclination.

Прочитал [интересную статью Егора](https://www.yegor256.com/2023/08/08/two-step-initialization.html) [Бугаенко.](https://www.yegor256.com/2023/08/08/two-step-initialization.html) Кто не знает, это директор лаборатории в Huawei, топ контрибьюторов GitHub, автор известного труда «Elegant …
7 months, 2 weeks ago

⚡️IL2CPP Часть 2

Вторую часть я бы хотел посвятить практической части.
А именно: небольшим моментам, которые могут оптимизировать код c++ (в который превращается наш проект на Unity).

  1. В IL2CPP явное приведение стоит дорого! ((FooClass) obj)
    Если посмотреть как себя ведет приведение типов в сгенерированном c++ коде, то можно увидеть:
    Используется static bool переменные и глобальные переменные, которые не лежат в кешах цп (следовательно обращение к ним будет затратно).

Кеш миссы, ведут к тому, что (примерно 100 наносекундах на один промах в кэше) мы, вероятно, ждем в оперативной памяти в сумме 500 наносекунд.
Если, к примеру, мы выполняем извлечение квадратного корня из 32-битных чисел с плавающей запятой за 14 циклов или 7 наносекунд.
То мы теряем в общей сложности примерно извлечение 71 квадратного корня при одном кастинге.🤬

  1. Ключевое слово as в IL2CPP также вызывает +- ту же цепочку методов. А значит также затратна.

  2. Все проверки, о которых я говорил ранее: проверки на Null, деление на ноль и проверку выхода за границы массива, также можно отключить, и мы повысим производительность.

  3. Дженерики, исключения, строковые литералы. Что?🤨 Да, даже они. Все они опять используют static bool. И опять лишние условно 100 наносекунд

  4. Вызов метода интерфейса обходится немного дороже, чем вызов виртуального метода.

Пока думаю достаточно)) Вообще особенностей гораздо-гораздо больше.
Telegram не та площадка, где можно подробно разложить c++ код и продемонстрировать то, что я пишу.
Но думаю, что в будущем перейду на youtube где мы все посмотрим.☺️

Значит ли это, что нужно отказаться от дженериков, например? Или от интерфейсов, приведений типа? Нет, безусловно нет
НО важно понимать, что ты делаешь и когда. И к чему это приводит.
Соответственно, если нужно что-то ускорить — Welcome! ______
Объясню, что такое кеш мисс (Cache Miss(промах кэша)) — он случается, когда запрашиваемые данные отсутствуют в кэше и их нужно подгружать из основного источника.
Например, у любого процессора есть кэш, в котором он хранит недавно прочитанные блоки оперативной памяти и обращение к кэшированным данным происходит существенно быстрее, чем "перекачивание" этих же данных из оперативной памяти на кристалл процессора.

7 months, 2 weeks ago
***😊***Связанность и Связность

😊Связанность и Связность
Связанность = coupling
Связность = cohesion

Часто при проектировании системы или даже продукта возникает вопрос оценивания связности и связанности, а при общении часто понимаю, что люди ошибаются в терминах.

1. Связанность модулей — зацепление. Это то **насколько при изменении одного модуля придется менять другой.
Значит, чем слабее связанность (зацепление) тем лучше.

  1. Связность - плотность. Настолько четко конструкции (условно методы) внутри класса сфокусированы на решении его задачи.
    Значит, чем лучше код, тем сильнее связность.**Связанность применяется когда говорим о модулях. О связи между несколькими компонентами. А связность чаще всего о чем-то поменьше, например класс.
    В GRASP как раз есть два таких правила: Low Coupling, High Cohesion. Советую ознакомиться и с книгой. (Крэг Ларман «Применение UML и шаблонов проектирования»)

Лайфах как запомнить: слово «связность» меньше, поэтому говорим о чем то поменьше.
Слово «связанность» больше, значит говорим о чем-то побольше, о взаимосвязях в модулях.

Идеальная картина та, которая представлена на изображении. В ней как раз слабый coupling, сильный cohesion.

7 months, 2 weeks ago
7 months, 2 weeks ago

👊Боевой совет для собеседований на Unity #3
Благодаря постам выше вы знаете, как завоевать доверие у нетехнической стороны компании.

Если вы подготовили ответы на вопросы и ставите акценты, почему именно вы нужны компании, то это уже полдела.

Ещё нужна харизма и уверенность. Плюс хорошее настроение, улыбка, добропорядочность.

— На собеседовании очень важно не грустить. Нужно улыбаться, соглашаться.
— Старайтесь не искать негативные моменты, которые были озвучены в компании (например когда вы узнаете технический стек. Или реакция на тех. вопросы. Или "глупые, никому не нужные" тех. вопросы).
— Старайтесь не спорить. Если есть момент, где вы не согласны, лучше озвучить это потом вживую, когда будете там работать).
Или, если что-то критичное, — вежливо откажитесь от предложения о работе в письме после собеседования.
Никакого смысла в споре нет. Показать свое "я" вы можете по-другому, проявив креативность и остроту ума в ответах на вопросы.

Безусловно, многое зависит от вашего характера.
Если есть моменты переживания, то это нормально. Но нужно постараться совладать с собой.
Очень помогают практики с дыханием и его задержкой.

И напоследок. Все, что было описано направлено на одну цель — пройти собеседование на работу.

Поэтому держите в голове: "Какая у меня цель?"

#interview #advice

7 months, 2 weeks ago
**Про IL2CPP** [1/4]

Про IL2CPP [1/4]

Unity имеет два скриптовых бекенда: Mono и IL2CPP, каждый из которых использует разные методы компиляции:

— Mono использует JIT-компиляцию и компилирует код по требованию во время выполнения.
— IL2CPP использует AOT (Ahead of Time, досрочная) и компилирует все приложение перед его запуском.

IL2CPP (Intermediate Language To C++) преобразует IL-код (который используется в Unity) в C++ код, который может быть компилирован в машинный код для различных платформ.
Это позволяет использовать функции и библиотеки C++, что в частности может привести к улучшению производительности и оптимизации кода.

При запуске сборки с использованием IL2CPP Unity автоматически выполняет следующие шаги:
1) Компилятор Roslyn компилирует код вашего приложения на C#.
2) Unity применяет стриппинг - удаление лишнего кода для сжатия приложения.
3) IL2CPP преобразует все управляемые сборки в стандартный код C++.
4) Компилятор C++ компилирует сгенерированный код C ++ и исполняемую часть IL2CPP с помощью собственного платформенного компилятора.
5) Unity создает либо исполняемый файл, либо библиотеку DLL, в зависимости от целевой платформы.
Очевидно, этот алгоритм занимает чуть больше времени, чем классическая Mono компиляция.

Помимо оптимизации, код на C++ будет иметь повсюду вставленные проверки на Null, деление на ноль и проверку выхода за границы массива.
Эти проверки можно выключить, дав методу или классу атрибут по типу

[Il2CppSetOption(Option.NullChecks, false)] public static string MethodWithNullChecksDisabled() { var tmp = new object(); return tmp.ToString(); }

Это только первая часть.

Завтра будет пост по GC, вторая часть)
Дальше — интересней!) 👊

7 months, 2 weeks ago
We recommend to visit

Официальный канал @CryptoBot.

Возможности бота: @CryptoBotTips

Поддержка: @CryptoSupportBot

Наш чат: @CryptoBotRussian

X: x.com/CryptoBotHQ

Last updated 1 week, 2 days ago

#1 канал о блокчейне, криптовалютах и децентрализованных финансах.

🔥 Реклама — @DCTeam

Last updated 21 hours ago

Канал о TON и все что с ним связано:
1. Аналитика
2. Инсайды
3. Авторское мнение

Ведро для спама: @ton_telegrambot

Бот с курсами криптовалют: @TonometerBot

Чат: @chaTON_ru

Админ: @filimono

Last updated 4 days, 17 hours ago