if lock if
У меня в BECS очень много всяких вариантов локов, например, для ресайзов вида:
Но поскольку мне нужно многопоточность, самый простой вариант обернуть это в лок:
Но поскольку операция создания объекта происходит крайне редко (тут даже скорее лучше бы подошел пример с ресайзом массива), то какой смысл тратить перф на блокировку? Никакого.
Получается такая матрешка, но давайте резберемся что будет для нескольких потоков:
1. Поток №1 входит в условие if (obj.isCreated == false);
2. Поток №2 тоже может успеть войти в это условие;
3. Поток №1 блокирует объект;
4. Поток №2 ожидает снятия блокировки;
5. Поток №1 создает объект и записывает его;
6. В этом месте любое количество других потоков может войти в метод и уже использовать наш объект;
7. Поток №1 выходит из блокировки, освобождая поток №2
8. Поток №2 проверяет еще раз if (obj.isCreated == false) и оказывается, что ничего делать уже не нужно.
Таким образом если какая-то операция происходит довольно редко, то такая реализация ленивой инициализации в многопоточной среде увеличит производительность.
Естественно, я не использую конструкцию lock, заменяя ее CompareExchange, но для примера привожу именно lock.
Важное уточнение: isCreated = true вашего объекта в конструкторе должен идти последним, т.е. когда мы уже можем использовать объект.
#multithreading #csharp #lock
У меня в BECS очень много всяких вариантов локов, например, для ресайзов вида:
if (obj.isCreated == false) {
obj = new Obj();
}
Но поскольку мне нужно многопоточность, самый простой вариант обернуть это в лок:
lock (lockObj) {
if (obj.isCreated == false) {
obj = new Obj();
}
}
Но поскольку операция создания объекта происходит крайне редко (тут даже скорее лучше бы подошел пример с ресайзом массива), то какой смысл тратить перф на блокировку? Никакого.
if (obj.isCreated == false) {
lock (lockObj) {
if (obj.isCreated == false) {
obj = new Obj();
}
}
}
Получается такая матрешка, но давайте резберемся что будет для нескольких потоков:
1. Поток №1 входит в условие if (obj.isCreated == false);
2. Поток №2 тоже может успеть войти в это условие;
3. Поток №1 блокирует объект;
4. Поток №2 ожидает снятия блокировки;
5. Поток №1 создает объект и записывает его;
6. В этом месте любое количество других потоков может войти в метод и уже использовать наш объект;
7. Поток №1 выходит из блокировки, освобождая поток №2
8. Поток №2 проверяет еще раз if (obj.isCreated == false) и оказывается, что ничего делать уже не нужно.
Таким образом если какая-то операция происходит довольно редко, то такая реализация ленивой инициализации в многопоточной среде увеличит производительность.
Естественно, я не использую конструкцию lock, заменяя ее CompareExchange, но для примера привожу именно lock.
Важное уточнение: isCreated = true вашего объекта в конструкторе должен идти последним, т.е. когда мы уже можем использовать объект.
#multithreading #csharp #lock
🔥21👍8🤔4🥰3
https://www.youtube.com/watch?v=eQODSTApGrU
Поговорили тут немного про использование unsafe.
#record #stream
Поговорили тут немного про использование unsafe.
#record #stream
YouTube
unsafecsharp: Как и для чего использовать Unsafe в C#
Разберем примеры unsafe кода, примеры использования
Телеграм канал: @unsafecsharp
Телеграм канал: @unsafecsharp
🔥36👍10💋3🥰2
Unity Collections
Тут что-то на днях я заглянул в UnsafeQueue и заметил, что юнитехи ее переработали, и зачем-то добавили пул. Ну вот я не знаю что у них в головах когда они так делают, но в итоге если делать new UnsafeQueue(Allocator.Temp), то эту коллекцию обязательно нужно освобождать через Dispose, иначе эти самые пулы не будут возвращены.
Тут я подумал, может я че не заметил в изменении доки?
Да нет, все как и раньше: если создал Temp, то освобождать ее нет необходимости.
Короч, я очень надеюсь, что они исправят эту ошибку и вообще уберут пул, который зачем-то запихнули в Queue, при этом только туда, т.е. UnsafeList, например, остался как и был.
#unity #unsafe
Тут что-то на днях я заглянул в UnsafeQueue и заметил, что юнитехи ее переработали, и зачем-то добавили пул. Ну вот я не знаю что у них в головах когда они так делают, но в итоге если делать new UnsafeQueue(Allocator.Temp), то эту коллекцию обязательно нужно освобождать через Dispose, иначе эти самые пулы не будут возвращены.
Тут я подумал, может я че не заметил в изменении доки?
Because a Temp allocator gets discarded as a whole, you don't need to manually deallocate Temp allocations, and doing so does nothing.
Да нет, все как и раньше: если создал Temp, то освобождать ее нет необходимости.
Короч, я очень надеюсь, что они исправят эту ошибку и вообще уберут пул, который зачем-то запихнули в Queue, при этом только туда, т.е. UnsafeList, например, остался как и был.
#unity #unsafe
🤔29👍9🔥4
https://www.youtube.com/watch?v=s0ABebPu4Vg
Поговорили немного про многопоточность, блокировки и прочие штуки 🙂
#record #stream
Поговорили немного про многопоточность, блокировки и прочие штуки 🙂
#record #stream
YouTube
unsafecsharp: Многопоточность и как ее использовать
Разберем что же такое lock, как избежать блокировок, поговорим немного о cache line и как же работать с несколькими потоками.
Телеграм канал: @unsafecsharp
Телеграм канал: @unsafecsharp
🔥25❤3👍3💅3🍓1💋1
SerializeReference
Я уже рассказывал про этот аттрибут, который позволяет серилизовать любую структуру. Но недавно я столкнулся с интересным багом, т.к. по мне так это именно баг:
Если добавить поле с именем
На всякий случай я заглянул в доку и не нашел там ничего про то, что нельзя использовать поле с этим именем.
#unity #bug #serializereference
Я уже рассказывал про этот аттрибут, который позволяет серилизовать любую структуру. Но недавно я столкнулся с интересным багом, т.к. по мне так это именно баг:
public interface IMyData {}
[System.Serializable]
public struct MyStruct {
[SerializeReference]
public IMyData data;
public int references;
}
public MyStruct[] items;
Если добавить поле с именем
references в эту же структуру где у вас будет SerializeReference, то при компиляции будет ошибка The same field name is serialized multiple times с указанием именно на поле references.На всякий случай я заглянул в доку и не нашел там ничего про то, что нельзя использовать поле с этим именем.
#unity #bug #serializereference
🤔24🤣12🔥6👍5😁2
Assembly Definition и Assembly Definition ReferenceЯ уже затрагивал эту штуку несколько раз, но совсем забыл рассказать про
Assembly Definition Reference.Если
Assembly Definition - это непосредственно либа, которая собирается из папки.То
Assembly Definition Reference - это продолжение этой же либы, но уже в другой папке.Итого можно сделать несколько папок, которые будут собираться в одну библиотеку.
Это на самом деле дает возможность использовать internal классы/структуры/методы из пакетов, которые вы используете в проекте. Т.е. просто сделать указание на либу из пакета и можно спокойно использовать нужные internal методы.
#unity #asmdef #asmref
👍29🔥12🤔4❤🔥1
Буду на gdc, если кто-нибудь хочет встретиться - буду рад поболтать, обсудить проекты или еще что-нибудь :)
Если хотите что-нибудь конкретное узнать со стендов/докладов, могу сделать несколько постов.
#gdc2025
Если хотите что-нибудь конкретное узнать со стендов/докладов, могу сделать несколько постов.
#gdc2025
🔥43👍13🥰2
Unity 2d
Sprite Mask
Теперь можно использовать любой 2d renderer в качестве маски через mask source, включая tilemap.
Sprite Libraries
Выше уровнем, чем SpriteAtlas, по сути дает возможность нормально контролировать использование ресурсов.
Через Sprite Resolver можно подставлять части.
Pixel art
Поддержка aseprite, новый importer, включая анимации.
Performance
Завезли SRP Batcher.
2D light batching debugger по слоям.
Physics
Появились Layer overrides.
Simulation layers позволяют отключить слои симуляции.
Delaunay tessellation для генерации коллайдеров.
Rigidbody slide functions.
Advanced
Sprite shape geometry creator позволяет делать сложные формы.
ScriptablePacker позволяет интегрировать свой алгоритм паковки.
2D Renderer поддерживает Render Graph.
WIP
Render as 2D - позволяет рендерить 3д объекты как 2д с сортировкой, тенями, светом и пр.
#gdc2025
Sprite Mask
Теперь можно использовать любой 2d renderer в качестве маски через mask source, включая tilemap.
Sprite Libraries
Выше уровнем, чем SpriteAtlas, по сути дает возможность нормально контролировать использование ресурсов.
Через Sprite Resolver можно подставлять части.
Pixel art
Поддержка aseprite, новый importer, включая анимации.
Performance
Завезли SRP Batcher.
2D light batching debugger по слоям.
Physics
Появились Layer overrides.
Simulation layers позволяют отключить слои симуляции.
Delaunay tessellation для генерации коллайдеров.
Rigidbody slide functions.
Advanced
Sprite shape geometry creator позволяет делать сложные формы.
ScriptablePacker позволяет интегрировать свой алгоритм паковки.
2D Renderer поддерживает Render Graph.
WIP
Render as 2D - позволяет рендерить 3д объекты как 2д с сортировкой, тенями, светом и пр.
#gdc2025
👍37🔥19❤3🥰1🎉1🤗1🫡1
Performance tips & tricks
Advanced profiling
CPU Native profilers (ios Instruments, android studio, etc)
Или
Use Superluminal (подключается к юнити, показывает методы включая cpp)
CPU issues
Shader variants (их может быть тысячи)
Используйте shader prewarming
SVC имеют метод WarmUp
GraphicsStateCollections заменяют SVC в unity 6
MPB не используйте в URP /HDRP
MeshRenderer в некоторых случаях будет лучше: если рисуем одну мешку больше 1к раз, используем одинаковые свойства
Используйте RenderMeshInstanced или BRG
Draw calls: используйте HLOD
Используйте меньше камер, каждая камера имеет большой оверхед
Используйте разные SRP для разных камер
Resident Drawer
Split jobs
GPU issues
Overdraw: прозрачные объекты перекрывают друг друга, исключите полностью прозрачные объекты
Используйте renderdoc
Quad Overdraw: используйте LOD/HLOD
Shaders: исключайте if (например shader keywords), используйте half, int16
Memory
Используйте native profilers
Избегайте коротких (по времени) и длинных аллокаций вместе
Используйте unmanaged аллокации
Используйте Memory profiler memory map
Используйте GC.Alloc call stacks feature
Asset duplication - ресурсы будут загружены в память несколько раз (при использовании бандлов)
Shaders
Используйте shader_feature вместо multi_compile
Используйте IPreprocessShaders API
#gdc2025
Advanced profiling
CPU Native profilers (ios Instruments, android studio, etc)
Или
Use Superluminal (подключается к юнити, показывает методы включая cpp)
CPU issues
Shader variants (их может быть тысячи)
Используйте shader prewarming
SVC имеют метод WarmUp
GraphicsStateCollections заменяют SVC в unity 6
MPB не используйте в URP /HDRP
MeshRenderer в некоторых случаях будет лучше: если рисуем одну мешку больше 1к раз, используем одинаковые свойства
Используйте RenderMeshInstanced или BRG
Draw calls: используйте HLOD
Используйте меньше камер, каждая камера имеет большой оверхед
Используйте разные SRP для разных камер
Resident Drawer
Split jobs
GPU issues
Overdraw: прозрачные объекты перекрывают друг друга, исключите полностью прозрачные объекты
Используйте renderdoc
Quad Overdraw: используйте LOD/HLOD
Shaders: исключайте if (например shader keywords), используйте half, int16
Memory
Используйте native profilers
Избегайте коротких (по времени) и длинных аллокаций вместе
Используйте unmanaged аллокации
Используйте Memory profiler memory map
Используйте GC.Alloc call stacks feature
Asset duplication - ресурсы будут загружены в память несколько раз (при использовании бандлов)
Shaders
Используйте shader_feature вместо multi_compile
Используйте IPreprocessShaders API
#gdc2025
🔥22👍10🥰1
Тут один хороший человек расписал работу моего аллокатора, который я использую в ME.ECS и ME.BECS. За что ему отдельное спасибо :)
P.S: Для чего нужен аллокатор и сорсы в этом посте: https://news.1rj.ru/str/unsafecsharp/49
#allocator #memory
P.S: Для чего нужен аллокатор и сорсы в этом посте: https://news.1rj.ru/str/unsafecsharp/49
#allocator #memory
🔥23👍10🤯7❤3
Animator
На самом деле тут есть 2 момента, которые я бы хотел обозначить с точки зрения кода:
1. У аниматора есть
Например: при клике по кнопке нужно проиграть анимацию, которая висит на рут объекте, но будет затрагивать объект, который мы создаем в иерархии. Тогда перед тем как "заказывать" стейт для проигрывания у аниматора, нужно сделать animator.Rebind().
2.
#animator #unity #animations
На самом деле тут есть 2 момента, которые я бы хотел обозначить с точки зрения кода:
1. У аниматора есть
Rebind. Используется в том случае, если анимация затрагивает объекты, которые создаются динамически, но на момент запуска аниматора их еще не было. Например: при клике по кнопке нужно проиграть анимацию, которая висит на рут объекте, но будет затрагивать объект, который мы создаем в иерархии. Тогда перед тем как "заказывать" стейт для проигрывания у аниматора, нужно сделать animator.Rebind().
2.
keepAnimatorStateOnDisable. Это такая штука, которая позволяет "сохранить" (точнее не сбрасывать) состояние аниматора при выключении и последующем включении объекта, на котором есть аниматор. На самом деле если перейти в debug-режим инспектора, то эту галочку можно включить там и без кода.#animator #unity #animations
👍27🔥13❤1🫡1
ME.BECS #1: Создание и инициализация проекта
@heavyfront сделал то, до чего у меня не доходили руки;) За что ему огромное спасибо!
https://youtu.be/PCdhnXEjRQI
#becs #tutorials
@heavyfront сделал то, до чего у меня не доходили руки;) За что ему огромное спасибо!
https://youtu.be/PCdhnXEjRQI
#becs #tutorials
🔥15👍11🥰2😱2
🔥33👍10🥰3
🔥15👍4🥰2
🔥16👍5🥰3💋3❤🔥1
UI.Windows
Рассказываю об основных возможностях UI.Windows
https://youtu.be/S6qLvaY204M
#tutorials #uiws #ui
Рассказываю об основных возможностях UI.Windows
https://youtu.be/S6qLvaY204M
#tutorials #uiws #ui
YouTube
unsafecsharp: Как использовать Unity UI.Windows
Как использовать UI.Windows, основные возможности
Телеграм канал: @unsafecsharp
https://github.com/chromealex/UI.Windows-submodule
Телеграм канал: @unsafecsharp
https://github.com/chromealex/UI.Windows-submodule
🔥16👍7❤4
ME.BECS #4: Query, Jobs. Network input events, transport.
https://youtu.be/dzUZorAW_uI
#tutorials #becs
https://youtu.be/dzUZorAW_uI
#tutorials #becs
🔥14❤5👍4🥴4🥰1
Я тут на выходных посидел и все таки добил вариант с детерминированным созданием сущностей в многопоточке.
Как это работает:
1. На этапе кодогена, разбираются все джобы и в IL смотрю сколько раз вызывается создание сущности в конкретной джобе. Данные кодогенятся в специальную табличку;
2. Когда мы в коде делаем AsParallel и Schedule для джобы - если для этой джобы больше одного создания сущности за одну итерацию, то резервируем столько сущностей, сколько необходимо чтобы обеспечить несколько потоков;
3. Каждый поток знает индекс смещения и итерацию, т.е. фактически знает откуда забрать новые id сущностей и какими они будут;
Какую проблему оно решает? Представим код джобы:
Если такой код запустить в однопоточке - будет 1, потом 2.
А вот если в многопоточке, то тут как повезет, первым будет id 1, а вторым что угодно, т.к. id 2 мог забрать себе уже другой поток.
Таким образом, если я буду знать сколько ентитей нужно создать за одну итерацию, то и проблем возникнуть не должно.
Ограничением становятся только циклы. Если при разборе IL, я вижу, что создание сущности вложено в цикл, то такую джобу зашедулить многопоточно будет нельзя (будет исключение).
Если хотите подробнее про IL и каким образом я разбираю код - могу сделать отдельный пост.
#becs #news
Как это работает:
1. На этапе кодогена, разбираются все джобы и в IL смотрю сколько раз вызывается создание сущности в конкретной джобе. Данные кодогенятся в специальную табличку;
2. Когда мы в коде делаем AsParallel и Schedule для джобы - если для этой джобы больше одного создания сущности за одну итерацию, то резервируем столько сущностей, сколько необходимо чтобы обеспечить несколько потоков;
3. Каждый поток знает индекс смещения и итерацию, т.е. фактически знает откуда забрать новые id сущностей и какими они будут;
Какую проблему оно решает? Представим код джобы:
var e1 = Ent.New(); // выдается id 1
var e2 = Ent.New(); // выдается id 2
Если такой код запустить в однопоточке - будет 1, потом 2.
А вот если в многопоточке, то тут как повезет, первым будет id 1, а вторым что угодно, т.к. id 2 мог забрать себе уже другой поток.
Таким образом, если я буду знать сколько ентитей нужно создать за одну итерацию, то и проблем возникнуть не должно.
Ограничением становятся только циклы. Если при разборе IL, я вижу, что создание сущности вложено в цикл, то такую джобу зашедулить многопоточно будет нельзя (будет исключение).
Если хотите подробнее про IL и каким образом я разбираю код - могу сделать отдельный пост.
#becs #news
🔥39❤5👍4👀2