C# Heppard
1.56K subscribers
74 photos
2 files
122 links
25 способов эффективно использовать .NET

Поддержать канал можно тут: https://sponsr.ru/sharp_heppard
Download Telegram
Работа с Excel #решение #память

Нашёл тут очень интересную библиотеку для работы с xlsx.

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

В целом же, рекомендую для рассмотрения замены EPPlusFree и ClosedXML, если у вас есть проблемы с производительностью. Или, например, памятью. Я это к чему. В EPPlus у нас файлик в 20 мб внезапно распаковался в какое-то страшное количество мегабайтиков, и мы упали с OutOfMemoryException.
🔥9👍4
Коллеги, если кто-то ходит в ClickHouse c помощью библиотеки у которой больше всего звёзд в github, то я вас немного расстрою. Кажется, это всего-лишь обёртка над HTTP. Причём не самая быстрая. Напомню, что ClickHouse поддерживает тот же GRPC.

#решение #хранилище
Продолжая тему с HTTP, хочется напомнить, что Trino поддерживает HTTP. Ну если вдруг вы не знаете, как к нему легко подключиться.

#решение #хранилище
Поиск пути #игра #алгоритм #память

Есть такой алгоритм, называется A-Star (A*) - он нужен для поиска оптимального пути в 2D пространстве. Если вы не знали, то для него есть хорошая штука в базовых коллекциях .NET, называется PriorityQueue (завезли в .NET 6). Мотивация, в принципе, была очевидна - очень востребованная коллекция в играх (привет Unity).

Очередь с приоритетами - основной элемент алгоритма поиска пути. Когда я отчаиваюсь писать для энтерпайза и начинаю писать свою стратегическую игру, я снова и снова воспроизвожу A*. Про очередь с приоритетами недавно писал некий Степан, а до него писали вот тут.

Кстати, этот же алгоритм очень полезен на собеседованиях, где есть алгоритмическая секция. Например, знание устройства очереди с приоритетами помогало мне на собесах в Яндекс или Тинькофф.
🔥10👍5
Копирование массива #скорость #бенч

А вот Array.Copy. Мы можем соревноваться с ним в виде поэлементного копирования массива или, даже, сделать буффер на стеке через stackalloc и пытаться перемещать через него.

Увы, не поможет. Array.Copy будет быстрее за счёт, например, memmove. Там есть ещё пара ухищрений, но я рекомендую посмотреть их лично, перейдя к исходникам.

По вопросам реализации в своё время был интересный тред на github. Там такая масса подробностей, что мозг может задымиться.

Для нас это всё должно значить примерно следующее: по возможности нужно использовать Array.Copy (пусть даже это в виде stream.ReadAsync(buffer), а не писать велосипеды в виде побайтного копирования потока.
👍9🔥4
Доказательство существования поколений #отдых #бенч #память

Помню, на одном собесе меня попросили доказать наличие нескольких поколений памяти в .NET. Я изобразил вот такое, отталкиваясь от знаний, какие объекты попадают в LOH. Может быть кому-то понадобится.


[SimpleJob(RuntimeMoniker.Net80)]
[MeanColumn, MemoryDiagnoser]
public class LohArray
{
[Params(84900, 85000)] public int Length { get; set; }

[Benchmark]
public int LohIliNet()
{
var array = new byte[Length];
return array[0] % 2;
}
}

Сегодня я пересматриваю А.Жмур (Pragmatic memory management) и, о чудо, внезапно вспомнил, откуда я взял этот код. Как интересно работает память человека. Как в .NET)
👍13🤯4
Static в анонимных функциях #отдых #бенч

Представляется, что ключевое слово static в анонимных функциях - декларация о намерениях, не более. То есть мы как бы говорим, что по нашему мнению в данном месте точно не надо захватывать контекст и создавать объект аллокации. Бай дизайн типа.

Т.е. это не про скорость и аллокацию тема.

Код бенчмарка в чатике.

Ещё почитать про это можно в соседнем канале.
👍114
Графика для самых маленьких #графика #лекция #игра

Рекомендую хороший набор роликов по OpenGL. Лектор явно с математическим складом ума, много рассказывает о том, как это должно работать, а потом реализует в коде. Примеры с использованием OpenTK. Есть и примеры с Monogame.
👍11
Докупить память или написать эффективно? #отдых

05.03.2024 в рамках процесса *оптимизации* использования ресурсов кластера kubernetes (***) будут выключены сервисы, развернутые в следующих неймспейсах...


Когда я получаю подобные сообщения от корпоративной инфраструктуры, либо от команды RE, я всегда вспоминаю один диалог. Состоялся он, ох, уж сколько лет прошло, на одном локальном митапе. Я, как всегда, вещал про то, что было бы неплохо писать оптимальный код, в том смысле, чтобы экономить байтики и наносекунды. То да сё, слайды, смешки, лёгкое непонимание в глазах слушателей. Наконец, секция вопросов и ответов.

Я хорошо помню этого мужика, который во время доклада делал фейс-палмы, громко ёрзал стулом, сидел в телефоне. Я ждал вопросов от него и дождался.

- Скажите, а зачем всё это?
- В смысле, - не понимаю я.
- В том смысле, - сказал он авторитетно, - что это всё не нужно. Проще купить плашку в 16 ГБ памяти, чем всё это знать.

Я тогда был молод, поэтому мой ответ был слаб. Жаль. Мне кажется, что в тот момент некоторые люди разочаровались в теме. Теперь я умный, теперь я знаю что ему ответить. Ну про то, что у нас k8s, 800m процессора и 2048Mi памяти. А чтобы просить больше, надо согласования, утверждения и вообще.

Где этот дядя с 16 ГБ настоящей оперативки и, наверное, с двумя-тремя настоящими ядрами?
Отдай мне их, дядя, я всё прощу.
👍15😁11🤯1
Читая некоторые параллельные каналы а-ля .NET Разработчик или StepOne, я иногда удивляюсь. Мне кажется, коллеги иногда не внимательно слушают RadioDotNet. В очередной раз хочу его прорекламировать.

Это подкаст про:
- Новости мира .NET.
- Новинки языка C#.
- Новинки ASP.NET, EF и прочего.
- Новинки популярных библиотек.
- Обзоры статей известных авторов о мире .NET.

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

Для состоятельных парней, есть возможность поддержать Анатолия и Игоря на boosty. За это присылают подарки, дают доступ к болталке и ко всяким записям вне выпуска. Иногда там бывает интересно. Ребята молодцы, делают хорошее дело.

Считаю RadioDotNet одним из лучших каналов для получения оперативной информации о мире .NET.

P.S.: А вот этот канал, кажется, опережает даже RadioDotNet. Евгений молодец. Будь как Евгений.
P.P.S: И как Сергей и Степантут) тоже будь. Я вот их немного подначиваю, но они хорошие!

#лекция #статья
👍18🔥2
Свой ConcurrentDictionary #память #лекция

Кстати, ещё один камень в огород тех, кто считает, что проще купить плашку памяти, чем заняться оптимизациями.

Это скриншот из доклада Антона Нечуговских (Контур) по поводу своей собственной имплементации ConcurrentDictionary. Только и нужно - инструменты анализа, знание устройства словарей, знание модели памяти, понимание профиля нагрузки, аккуратный код.

Как результат: потребление памяти в кластере снизилось на 30-40%. Неплохо так сэкономили. Я не знаю сколько это в деньгах, но то, что они сэкономлены - нет никаких сомнений.
👍21🔥7👎1
Несколько solution в одном Rider #решение

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

Подключаем как Existing project, перезагружаем IDE (не знаю зачем, но надо) - вуаля. Иногда есть какое-то странное поведение - не получается ткнуть и переключиться на нужную ветку. Но, в целом, пользоваться можно.

Чтобы редактировать проекты локально и не связываться с пушингом в nuget каждого изменения, подключаем библиотеки вот так:


<ItemGroup>
<PackageReference
Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"
Include="***.Data"
Version="1.0.1"/>
<ProjectReference
Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"
Include="..\..\локальный_путь.Data.csproj"/>
</ItemGroup>


То есть в релизе будет библиотека из nuget'a, а в debug - локальная копия.

P.S.: Удалить проект можно в настройках: Version Control -> Directory Mappings. В окне нажимаем минусик, чтобы удалять репозитории.
🔥15👍4👎1🤯1
Где находится эффективный код #философия

737. Кажется, назрел вопрос - а кто, я, собственно, такой.

1. Привет, я - Кирилл.
2. Я программист, как и вы.
3. У меня есть бзик - производительность.
4. При этом, я не фанатик, который хочет заоптимизировать всё подряд. Я не тот, кто давит на окружающих - оптимизируй! Я имею возможность, но не делаю этого.
5. Я тот, кто хочет перевести код из состояния "не оптимальный код" в "оптимальный код" (см. рисунок - точки А в B). Максимум, дойти до "оптимизированный код" (С).
6. Но не для всех случаев. Часто, я бенчмаркаю ради того, чтобы исследовать возможности .NET.
7. Я стараюсь блюсти тему канала. Я не душню, я просто держусь темы. Мне кажется это важно, т.к. каналов про общие знания о .NET десятки.
8. Также, я понимаю бизнес. Я пять лет как lead с разными размерами команды. И да, я понимаю, что знания о производительности - дорогие знания, не нужные многим программистам, которые просто хотят заставить эту железку работать.

Я лишь хочу, чтобы мы заставляли эту железку работать оптимально.
👍451
Автор картинки выше - некий Алексей Шипилёв, хорошо известный в узких кругах Java-программистов. Картинка из доклада "Перформанс: что в имени тебе моём?", подстрочник которого находится тут.

Всем, кто так или иначе интересуется производительностью, я рекомендую просмотр и вдумчивое чтение. Это, так сказать, философия в облике фактов о Java. Одинаково применимая, кстати, к любым технологиям. В том числе к .NET.

Жаль, что в .NET нет таких людей. Некий Андрей Акиньшин мог бы, но ушёл в высокую статистику. Очень жаль. Его ранние доклады и заметки были очень классными. Посмотрите видосики. Особенно интересы замечания конферансье - "хорошй специалист, на самом самом, там, низком уровне".

#философия
🔥5👍41
Преждевременная пессимизация #философия #игра

Хотелось бы напомнить лекцию некого Дмитрия Иванова - "Сказки о преждевременной оптимизации". Лекция далёкого 2015 года. Коллега из компании, где производительность является ключевой фичей, напоминает нам о том, что мантра "Преждевременная оптимизация - корень всех зол" не всегда верна.

Например, мы исходим из предположения, что мы всегда найдём те 1-20% кода, которые тормозят всё и, конечно, поправим. Увы, нет. Иногда получается так, что 90% кода тормозят и мы вынуждены переписывать продукт почти заново.

Далеко ведь ходить не надо. Вот пример Cities Skylines 2. Вроде игрушечка, вроде могли всё написать нормально сразу. Но нет. Почему-то надо было выпуститься к определённой дате. Наверное, маркетологи и бизнес что-то знали и это, наверное, правильно. Увы, мне кажется, что они ошиблись - куча негативных отзывов тому пример.

Эта история ещё раз подтверждает тезис Дмитрия - "преждевременная пессимизация - корень зол". Т.е. если разработчики вообще не уделяют внимания элементарными подходам к производительности, то потребуется много человекочасов и масса изменений в коде, чтобы сделать всё более-менее оптимально. Это те же деньги, это то же время.

У меня в этой связи вопрос: понимает ли бизнес и донёс ли технический лидер эту простую мысль до руководства?
👍82😁1
Кто тут по поводу создания игр?

Хочу дать хороший ECS - DefaultECS. Как мне кажется, это интересная имплементация паттерна. Да, можно быстрее и лучше (см. LeoECS), но этот чувак как минимум реализовал DebuggerTypeProxy, а значит хотя бы пытался работать со своим решением.

Кстати, пользуясь случаем, хочу задать несколько вопросов по поводу ECS вообще их конкретных имплементаций. Я, знаете ли, иногда тоже балуюсь - вдруг получится реализовать лучше, чем решения коллег выше.

1. Так ли нужно создавать мультимиры? Это вопрос тестирования или чего-то ещё?
2. Там и сям у Entity есть версия. Зачем? Сделайте её ref struct и она никогда не будет хранится отдельно от контекста. Вопрос сравнения энтитей по версии стоит вообще?
3. Так ли нужно держать массив по количеству энтитей в каждом из ComponentPool? Конечно, это сильно быстрее. Но блин, если у меня 512 компонентов и 2 000 000 сущностей... Ну, например, карта 512 на 512 с юнитами.
4. Почему вы не делите данные на сущности игры, сущности настроек (ассеты?) и сущности изображений? Ну типа Actor/Asset/View?
5. Ну и, вопрос из кровавого энтерпайза, почему нет более-менее адекватной связи с подобиями MediatR? Это ж просто. И это отлично ложится на ECS.

#игра #решение
🤔4👍3🥰1
Бенч, который не работал #бенч #память #скорость

Однажды ко мне пришёл подпрыгивающий от изумления коллега, и показал примерно вот такой бенчмарк. По его замерам выходило, что Array.IndexOf по массиву с int в 300 раз быстрее применения того же метода на массиве uint. Более того, uint что-то там ещё и аллоцирует!

Ответ на эту загадку, конечно, элементарный, но показательный. Кто-то знает, в чём проблема?



Так как прошло достаточно времени, то можно написать правильный ответ прямо тут: дело в том, что при вызове Array.IndexOf(uint_array, 15), число 15 воспринимается компилятором как int, а значит перегрузка IndexOf<T>(T[] array, T value) не подходит. А значит, выбирается метод IndexOf(object[] array, object value).

Вывод: проблему сложно заметить, она не про перформанс, а про внимательность. Поэтому надо быть очень внимательным, когда мы пишем код, ведь нам очень легко получить проблему с производительностью на пустом месте. Вот как тут.

Код бенчмарка в комментариях.
👍7🤔53🔥2👎1😁1
Привет всем! Мы тут с коллегами задумали одну штуку сделать, но чтобы она была интересна и лучше понята, хотелось бы понять уровень присутствующей тут аудитории. Скажите, кем вы себя видите сейчас? Проголосуйте, это очень поможет. Спасибо!
Anonymous Poll
19%
Джун
38%
Миддл
25%
Сеньёр
5%
Тим лид
8%
Тех лид
0%
Менеджер
4%
Архитектор
👍2
Логирование и память #решение #память

Недавно разбирался со скоростью работы логирования, а также с тем, сколько логер потребляет памяти.

Эта подсистема не бесплатная и может влиять на производительность. Увы, от логов отказаться нельзя, поэтому хотелось бы всё-таки понять, как писать их быстро и экономно по памяти.

В качестве подопытного кролика выбрал Serilog, как наиболее часто используемую в разных проектах.

Победил чистый Serilog.ILogger, без прослойки в виде Microsoft.Extensions.Logging.ILogger. И дело не в скорости (она почти одинаковая), а в аллокации - у чистого Serilog она чуть меньше.

Однако, самым удивительным для меня оказалось то, что простое использование логгера было даже чуть экономичнее аналогов из советов про High-performance logging (ну все эти LoggerMessage.Define и LoggerMessageAttribute). Почему так получилось - понять с наскока не удалось.

Бенчмарк в комментариях.

P.S.: Для специалистов: писал просто в консоль через ConsoleSink, без изысков, поэтому цифры вот такие.
👍7🤔3
Массив на стеке #решение #память

С момента появления InlineArrayAttribute (мотивация) я хотел на него посмотреть в деле. Напомню, что это атрибут для структур, который "размножает" поле, в котором лежит "элемент массива". Также, атрибут добавляет индексатор для доступа к значениям, чтобы структура стала похожей на массив. Значения через индексатор попадают в сгенерированные поля, откуда могут быть получены позже.

И это всё на стеке, т.е. тот самый zero-allocation.

[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer10
{
private int _element0;
}


Единственная проблема: по поведению это массив, т.е. не List, который может расширяться. Это накладывает определённые ограничения на применение данной фичи. Конечно, выход существует и его придумали давно (см. тут): при достижении предела локальных полей мы создаём честный массив, куда складываем "избыток" значений.

С появлением InlineArray эти два подхода можно объединить. Получается неплохо: и быстрее и экономнее по памяти. Кода много, поэтому он тут.

Представить InlineArray как Span тоже просто - Span<int> span = myBuffer. Это позволит применить любые подходы работы со Span для всех структур, отмеченных InlineArrayAttribute.
🔥19👍32
Недавно опубликовали первое видео с последнего DotNext. Мне приятно, что первым опубликованным был некто Евгений, и про производительность.

Почему доклад может быть интересен?

1. Евгений отмечает, что BCL весьма оптимизирована для общих случаев и может считаться одной из самых хороших частей приложений на .NET в смысле производительности.

2. Поднимает важную тему сторонних библиотек, которые не могут или не хотят думать о производительности. Я это называю болезнью Middle-разработчика. Ты как бы уже можешь написать хорошее решение, но ещё не знаешь, как это сделать оптимально.

3. Евгений подтверждает мой личный опыт. Когда я в отчаянии, и в сотый раз пишу свою игру, я почти не пользуюсь сторонними решениями - ну кроме работы с OpenGL/DirectX. Как раз потому, что сторонние решения были написаны до эры Span'ов и прочего инструментария серьёзных парней для улучшения производительности.

4. Затрагивает особенности работы с ArrayPool в многопоточной среде, а также для маленьких массивов.

5. Упоминает интересную тему с LOH. Считается, что LOH это плохо. На самом деле плохо - это фрагментация кучи. Если у вас есть большой массив, его надо обязательно поместить в LOH и это будет более эффективно, чем заставлять GC спотыкаться о маленькие массивы.

6. Рассказывает про логирование. Детальный разбор будет позднее, если руки дойдут. Слушать интересно. Моя позиция неизменна - инфраструктура не должна аффектить производительность основного кода, либо делать это минимально. Будь то логирование или этот ваш MediatR.

7. Уделяет внимание InlineArrayAttribute.

8. Правильно указывает на то, что record мог бы стать отличным местом для кодогенерации. Когда фича только появилась, я так и думал, что это оттуда. Оказалось, увы, нет.

9. Упоминает меня с рассказом про S3. Надеюсь, видео скоро опубликуют.

#философия
👍155