Очень круто получилось с последним вопросом! Сейчас расскажу 🙂
Я хотел сделать основной фокус на отложенном выполнении (Deferred Execution) запросов в LINQ. Что значит “отложенное выполнение”? А это значит, что LINQ-запрос будет исполнен только там, где будет востребован непосредственный результат его выполнения. На 3-ей строке мы не получаем непосредственный результат выполнения, так как переменная result имеет тип IQueryable<Trades>. Проще говоря, мы только определили некоторый запрос, результат выполнения которого, нам потребуется в будущем.
Где же мы получаем результат? А получаем мы его на строках 8, 9 и 10. То есть, мы обращаемся к базе данных аж 3 раза. Если вы не делаете лабу для универа, то это непозволительная роскошь для профессиональной разработки. Как избежать трехкратного обращения к базе данных? Очень просто, нужно сразу запросить результат путём вызова метода ToList(). Таким образом, на 8, 9 и 10 строках, мы будем оперировать с данными, которые будут находиться в оперативной памяти и за ними не нужно “ходить” в базу данных.
Подписчик Руслан Петряев дал точный ответ в комментариях 👍
Кроме того, Руслан увидел еще одну проблему, которая была вызвана запросом приведения к List. Посмотрите на код, а нужен ли нам вообще этот List? Ведь нам совершенно не нужны данные из этого списка. Всё что нам нужно – это общее число отфильтрованных записей, минимальная и максимальная цены. Тогда зачем мы держим эти данные в оперативной памяти? Всё что нам нужно по логике метода:
1. Обратиться к базе данных.
2. Забрать Count, MinPrice и MaxPrice для отфильтрованных записей.
3. Сохранить данные в объект TradeData и вернуть его.
Это можно сделать так:
TradeData result = DbContext.Set<Trades>
.Where(t => t.Status == TradeStatuses.Completed)
.GroupBy(t => t.Status == TradeStatuses.Completed)
.Select(t =>
new TradeData
{
PricesCount = t.Count(),
PriceMin = t.Min(r => r.Price),
PriceMax = t.Max(r => r.Price)
}).FirstOrDefault();
Супер! От вопроса получилась двойная польза для всех!
Всем хорошего вечера! 👋
Я хотел сделать основной фокус на отложенном выполнении (Deferred Execution) запросов в LINQ. Что значит “отложенное выполнение”? А это значит, что LINQ-запрос будет исполнен только там, где будет востребован непосредственный результат его выполнения. На 3-ей строке мы не получаем непосредственный результат выполнения, так как переменная result имеет тип IQueryable<Trades>. Проще говоря, мы только определили некоторый запрос, результат выполнения которого, нам потребуется в будущем.
Где же мы получаем результат? А получаем мы его на строках 8, 9 и 10. То есть, мы обращаемся к базе данных аж 3 раза. Если вы не делаете лабу для универа, то это непозволительная роскошь для профессиональной разработки. Как избежать трехкратного обращения к базе данных? Очень просто, нужно сразу запросить результат путём вызова метода ToList(). Таким образом, на 8, 9 и 10 строках, мы будем оперировать с данными, которые будут находиться в оперативной памяти и за ними не нужно “ходить” в базу данных.
Подписчик Руслан Петряев дал точный ответ в комментариях 👍
Кроме того, Руслан увидел еще одну проблему, которая была вызвана запросом приведения к List. Посмотрите на код, а нужен ли нам вообще этот List? Ведь нам совершенно не нужны данные из этого списка. Всё что нам нужно – это общее число отфильтрованных записей, минимальная и максимальная цены. Тогда зачем мы держим эти данные в оперативной памяти? Всё что нам нужно по логике метода:
1. Обратиться к базе данных.
2. Забрать Count, MinPrice и MaxPrice для отфильтрованных записей.
3. Сохранить данные в объект TradeData и вернуть его.
Это можно сделать так:
TradeData result = DbContext.Set<Trades>
.Where(t => t.Status == TradeStatuses.Completed)
.GroupBy(t => t.Status == TradeStatuses.Completed)
.Select(t =>
new TradeData
{
PricesCount = t.Count(),
PriceMin = t.Min(r => r.Price),
PriceMax = t.Max(r => r.Price)
}).FirstOrDefault();
Супер! От вопроса получилась двойная польза для всех!
Всем хорошего вечера! 👋
👍36🔥9❤🔥1
#капитану_на_заметку
Очень часто, если коллекция не содержит искомых элементов, разработчик принимает решение вернуть null. Такое может быть, когда возвращается пустая выборка из БД, коллекция не может быть сгенерирована ввиду ошибочных условий и т. д. Подобное решение приводит к избыточным проверкам во внешнем коде, а также повышает вероятность появления исключения NullReferenceException. Хорошим тоном является возврат пустых коллекций. Старайтесь не возвращать null, когда ваши коллекции не содержат элементов ✅
Очень часто, если коллекция не содержит искомых элементов, разработчик принимает решение вернуть null. Такое может быть, когда возвращается пустая выборка из БД, коллекция не может быть сгенерирована ввиду ошибочных условий и т. д. Подобное решение приводит к избыточным проверкам во внешнем коде, а также повышает вероятность появления исключения NullReferenceException. Хорошим тоном является возврат пустых коллекций. Старайтесь не возвращать null, когда ваши коллекции не содержат элементов ✅
👍42🔥7🤔4
Всем привет! 👻
Продолжаем разбираться с LINQ и сегодня на повестке: группировка данных с помощью операции GroupBy. Если вы еще никогда не делали группировку данных, то этот гайд будет для вас красной ковровой дорожкой 🪄
Приятного просмотра! 💙
https://youtu.be/G7ytatcF7GY
Продолжаем разбираться с LINQ и сегодня на повестке: группировка данных с помощью операции GroupBy. Если вы еще никогда не делали группировку данных, то этот гайд будет для вас красной ковровой дорожкой 🪄
Приятного просмотра! 💙
https://youtu.be/G7ytatcF7GY
YouTube
C# LINQ GroupBy
В ролике рассмотрим использование LINQ для выполнения группировки данных с помощью операции Group By. Поговорим о том, зачем вообще нужно делать Group By. Посмотрим на суть через призму Transact SQL. Сделаем Group By в двух синтаксисах LINQ - Query Syntax…
👍22❤8
#codaza_спрашивает
Всем привет! 👋
Сижу я сегодня за компом, спокойно себе отмечаю день пельменей (сегодня день пельменей, если вы не знали) и мне звонит мой хороший знакомый Junior-разработчик Леонид. Лёня, рыдая в трубку, говорит: Уже который раз отклоняют merge в master с пометкой: 'Просадка в производительности'. Я посмотрел код, Лёня там чё-то шифрует-перешифрует.. наверное, ему так надо. Я ничего не понял, сказал что у меня в группе codaza отличные ребята и девушки, они точно разберутся, где у тебя там просадка в производительности.
Поможем Леониду? Или спасение утопающих — дело рук самих утопающих?
Всем привет! 👋
Сижу я сегодня за компом, спокойно себе отмечаю день пельменей (сегодня день пельменей, если вы не знали) и мне звонит мой хороший знакомый Junior-разработчик Леонид. Лёня, рыдая в трубку, говорит: Уже который раз отклоняют merge в master с пометкой: 'Просадка в производительности'. Я посмотрел код, Лёня там чё-то шифрует-перешифрует.. наверное, ему так надо. Я ничего не понял, сказал что у меня в группе codaza отличные ребята и девушки, они точно разберутся, где у тебя там просадка в производительности.
Поможем Леониду? Или спасение утопающих — дело рук самих утопающих?
👍28
Ура! 🎉
Леонид был спасён уже через 5 минут после публикации поста в пятницу. Всё благодаря опытным подписчикам #codaza, которые не дали джуну утонуть в неведомых дебрях асинхронного кода C#. Лёне одобрили его первый merge в master, от чего он начал праздновать День Пельменей с невиданным размахом!
В чём же была проблема? Почему злой тим лид отмахивался комментарием “Просадка в производительности” и отклонял merge? Всё дело в 3-ей и 5-ой строках исходного кода. Что делает Леонид:
1. Запускает асинхронное выполнение задачи GroupDataChunkAsync().
2. Ожидает окончания её выполнения.
3. Запускает асинхронное выполнение задачи CalculateCypherPhraseAsync().
4. Ожидает окончания её выполнения.
Все эти 4 действия выполняются последовательно. Но Лёня не задаётся вопросом, а нужно ли выполнять эти действия последовательно? Может быть, они могут быть выполнены параллельно? И, как вы уже догадались, эти действия могут быть выполнены параллельно. Мы можем вызвать асинхронное выполнение методов GroupDataChunkAsync() и CalculateCypherPhraseAsync() независимо, так как результат их выполнения потребуется только на 7-ой строке. То есть, следовало сделать так:
1. Запустить асинхронное выполнение задачи GroupDataChunkAsync().
2. Запустить асинхронное выполнение задачи CalculateCypherPhraseAsync().
3. Дождаться результата выполнения обеих задач и продолжить логику метода ProcessItemsAsync().
Один из вариантов, который предложил подписчик Eugene Potashkin, это воспользоваться статическим методом WhenAll, который реализован в классе Task. То есть, код будет выглядеть так:
Всем хорошего вечера! ☕️
Леонид был спасён уже через 5 минут после публикации поста в пятницу. Всё благодаря опытным подписчикам #codaza, которые не дали джуну утонуть в неведомых дебрях асинхронного кода C#. Лёне одобрили его первый merge в master, от чего он начал праздновать День Пельменей с невиданным размахом!
В чём же была проблема? Почему злой тим лид отмахивался комментарием “Просадка в производительности” и отклонял merge? Всё дело в 3-ей и 5-ой строках исходного кода. Что делает Леонид:
1. Запускает асинхронное выполнение задачи GroupDataChunkAsync().
2. Ожидает окончания её выполнения.
3. Запускает асинхронное выполнение задачи CalculateCypherPhraseAsync().
4. Ожидает окончания её выполнения.
Все эти 4 действия выполняются последовательно. Но Лёня не задаётся вопросом, а нужно ли выполнять эти действия последовательно? Может быть, они могут быть выполнены параллельно? И, как вы уже догадались, эти действия могут быть выполнены параллельно. Мы можем вызвать асинхронное выполнение методов GroupDataChunkAsync() и CalculateCypherPhraseAsync() независимо, так как результат их выполнения потребуется только на 7-ой строке. То есть, следовало сделать так:
1. Запустить асинхронное выполнение задачи GroupDataChunkAsync().
2. Запустить асинхронное выполнение задачи CalculateCypherPhraseAsync().
3. Дождаться результата выполнения обеих задач и продолжить логику метода ProcessItemsAsync().
Один из вариантов, который предложил подписчик Eugene Potashkin, это воспользоваться статическим методом WhenAll, который реализован в классе Task. То есть, код будет выглядеть так:
var groupTask = GroupDataChunksAsync(limit);Но более лаконичный вариант для данной конкретной реализации, предложил подписчик Руслан Петряев, где мы ожидаем результата выполнения задач непосредственно в вызове метода CheckEncryption(). Именно такую реализацию вы можете видеть на картинке выше. Так или иначе, оба варианты равнозначны и являются верными.
var phraseTask = CalculateCypherPhraseAsync(limit);
await Task.WhenAll(groupTask, phraseTask);
bool check = CheckEncryption(groupTask.Result, phraseTask.Result, limit);
Всем хорошего вечера! ☕️
👍42
#капитану_на_заметку
Всем привет!
Вы когда-нибудь заглядывали в System.Collections.Concurrent? Если вам доводилось писать многопоточный код, скорее всего вы там были неоднократно. Как только мне нужно писать многопоточный код, я на 99% уверен, что найду там структуру данных, которая решит большинство моих технических вопросов. Но есть коллекция, которая была разработана для очень специфических случаев - ConcurrentBag<T>.
ConcurrentBag<T> - это потокобезопасная реализация коллекции, оптимизированная для сценариев, в которых один и тот же поток будет создавать и потреблять данные, хранящиеся в ней.
Не рекомендуется использовать её без предварительного бенчмаркинга (замеров производительности). Если вы на 100% не уверены, что вам нужен именно ConcurrentBag<T>, отдайте предпочтение ConcurrentQueue<T> ✅
Всем привет!
Вы когда-нибудь заглядывали в System.Collections.Concurrent? Если вам доводилось писать многопоточный код, скорее всего вы там были неоднократно. Как только мне нужно писать многопоточный код, я на 99% уверен, что найду там структуру данных, которая решит большинство моих технических вопросов. Но есть коллекция, которая была разработана для очень специфических случаев - ConcurrentBag<T>.
ConcurrentBag<T> - это потокобезопасная реализация коллекции, оптимизированная для сценариев, в которых один и тот же поток будет создавать и потреблять данные, хранящиеся в ней.
Не рекомендуется использовать её без предварительного бенчмаркинга (замеров производительности). Если вы на 100% не уверены, что вам нужен именно ConcurrentBag<T>, отдайте предпочтение ConcurrentQueue<T> ✅
👍22
#капитану_на_заметку
Всем привет!
Вы уже знаете про очень полезный атрибут [DebuggerDisplay], который находится в пространстве имён System.Diagnostics? Если нет, то только что ваш дебаггинг вышел на уровень комфорт-класса 😎. С помощью этого атрибута мы можем указать способ отображения экземпляра класса во всплывающем окне дебаггера переменной, которое появляется при наведении курсора мыши.
+1 к премудростям дебага ✅
Поделитесь в комментариях какими полезными плюшками дебага вы пользуетесь и как они помогают решать возникающие проблемы.
Всем привет!
Вы уже знаете про очень полезный атрибут [DebuggerDisplay], который находится в пространстве имён System.Diagnostics? Если нет, то только что ваш дебаггинг вышел на уровень комфорт-класса 😎. С помощью этого атрибута мы можем указать способ отображения экземпляра класса во всплывающем окне дебаггера переменной, которое появляется при наведении курсора мыши.
+1 к премудростям дебага ✅
Поделитесь в комментариях какими полезными плюшками дебага вы пользуетесь и как они помогают решать возникающие проблемы.
👍63🌭1
#капитану_на_заметку
Всем привет!
Сегодня понедельник, а значит, нужно сделать еще один шаг к чистому коду. Вы слышали про конструкцию "
Это ещё одна суперспособность на уровне языка, которая делает программирование на C# по-настоящему чем-то особенным. Фишка в том, что мы можем фильтровать одно и то же исключение в блоке catch написав логическое выражение после оператора when. Это позволит ловить исключения одного и того же типа несколько раз, но фильтровать их по разным условиям. Данный подход сделает обработку исключений более структурной и системной. Мне нравится применять этот способ при обработке исключений, где логика обработки выходит за рамки определения типа исключения.
+1 к чистому коду ✅
Всем спокойной недели!
Всем привет!
Сегодня понедельник, а значит, нужно сделать еще один шаг к чистому коду. Вы слышали про конструкцию "
catch - when" в C#?Это ещё одна суперспособность на уровне языка, которая делает программирование на C# по-настоящему чем-то особенным. Фишка в том, что мы можем фильтровать одно и то же исключение в блоке catch написав логическое выражение после оператора when. Это позволит ловить исключения одного и того же типа несколько раз, но фильтровать их по разным условиям. Данный подход сделает обработку исключений более структурной и системной. Мне нравится применять этот способ при обработке исключений, где логика обработки выходит за рамки определения типа исключения.
+1 к чистому коду ✅
Всем спокойной недели!
👍61👎1
#codaza_спрашивает
Единственная причина для существования времени — чтобы все не случилось одновременно.
Альберт Эйнштейн
Всем привет!
Вчера не code review мне попался этот фрагмент кода. Метод проходит все модульные тесты. Я думаю, этот код сможет работать в production. Но... Кажется, он может тратить меньше времени... 🕙
Что думаете?
Любые улучшения только приветствуются 🙂
Единственная причина для существования времени — чтобы все не случилось одновременно.
Альберт Эйнштейн
Всем привет!
Вчера не code review мне попался этот фрагмент кода. Метод проходит все модульные тесты. Я думаю, этот код сможет работать в production. Но... Кажется, он может тратить меньше времени... 🕙
Что думаете?
Любые улучшения только приветствуются 🙂
👍17🎉1
Дааа... Кажется, из последнего примера ребята действительно выжали максимум. Это был мастер-класс по code review на высоком уровне 😎 Если вы не успели пробежаться по комментариям в предыдущем посте, то сделайте это.
Итак, главную проблему, первым заметил подписчик Алексей. Он увидел, что ожидание выполнения всех задач (
1. Обратиться ко всем имеющимся адресам, которые возвращает метод
2. Получить цены.
3. Вернуть одну цену.
Исходя из этой логики, ожидание всех цен абсолютно не требуется. Достаточно дождаться первой полученной цены и вернуть её. В этом нам поможет статический метод
Кроме того, Алесей предложил интересный алгоритм в случае одновременного обращения множества задач к одному и тому же адресу. Почитайте в комментариях, это может оказаться полезным.
На этом можно было закончить, но подписчик Alexander Radchenko заметил, что производительность можно поднять еще выше. Александр очень точно написал эту мысль в комментарии, поэтому я приведу цитату:
"Для ещё более быстрого решения надо добавить
Безусловно, в Enterprise разработке высоконагруженных систем, применить такой подход можно и нужно.
Завершил картину Руслан Петряев, предложив лаконичный вариант рефакторинга, который мне очень понравился:
Что же... Это была прекрасная командная работа. Всем нам очень повезло поучаствовать в этом полезном образовательном действе.
Спасибо! 👍
Итак, главную проблему, первым заметил подписчик Алексей. Он увидел, что ожидание выполнения всех задач (
Task.WhenAll()) - расточительно. По логике метода необходимо:1. Обратиться ко всем имеющимся адресам, которые возвращает метод
GetReplicationUrls(). Нам предоставляют множество резервных адресов, так как подразумевается, что некоторые могут быть недоступны по самым разным причинам (неисправен, находится на техническом обслуживании и т. д.). Разумеется, какие адреса и в какой момент они будут нам доступны, нам никто не сообщит.2. Получить цены.
3. Вернуть одну цену.
Исходя из этой логики, ожидание всех цен абсолютно не требуется. Достаточно дождаться первой полученной цены и вернуть её. В этом нам поможет статический метод
WhenAny(), который реализован в классе Task. Как только одна из задач будет завершена (цена получена), мы сразу же вернём её результат. В противном случае, мы будем вынуждены ждать столько времени, сколько занимает самая длительная задача, что увеличит время выполнения метода LoadItemPriceAsync(). Это будет особенно критично в тех случаях, когда будут сетевые задержки, таймауты и прочие сценарии, которые нельзя назвать успешными.Кроме того, Алесей предложил интересный алгоритм в случае одновременного обращения множества задач к одному и тому же адресу. Почитайте в комментариях, это может оказаться полезным.
На этом можно было закончить, но подписчик Alexander Radchenko заметил, что производительность можно поднять еще выше. Александр очень точно написал эту мысль в комментарии, поэтому я приведу цитату:
"Для ещё более быстрого решения надо добавить
CancellationToken чтобы останавливать цикл создания задач, как только первая задача выполнена и даже возможно прерывание уже выполняемых задач."Безусловно, в Enterprise разработке высоконагруженных систем, применить такой подход можно и нужно.
Завершил картину Руслан Петряев, предложив лаконичный вариант рефакторинга, который мне очень понравился:
private async Task<int> LoadItemPriceAsync(int itemId) =>Здесь не подразумевается обработка исключений. Это вам на самостоятельную работу 😉 Главная суть полностью раскрыта!
await Task.WhenAny(GetReplicationServersUrls()
.Select(async url => await LoadCurrentPriceAsync(itemId, url)))
.Result;
Что же... Это была прекрасная командная работа. Всем нам очень повезло поучаствовать в этом полезном образовательном действе.
Спасибо! 👍
👍43
#капитану_на_заметку
Всем привет!
Enum — это тип значения, определенный набором именованных констант и т.д. и т.п. далее по тексту из MSDN.
Это понятно. Есть вещи важнее понятных истин. Я говорю про дизайн. Вы знаете про хороший дизайн для Enum в C#?
Если нет, то скорее ловите, чтобы уже с понедельника ваш код стал выглядеть более профессионально.
Когда мы создаём новый Enum, мы всегда должны предоставлять значение по умолчанию (default value). В противном случае, первое значение в созданном Enum может быть рассмотрено как значение по умолчанию. Такое поведение нельзя считать верным и может вести к потенциально опасным и трудно отлаживаемым ошибкам.
Добавьте значение по умолчанию, которое будет адекватно вашему бизнес-домену. Это может быть "None", "Unknown" и прочее. Тем самым вы явно укажете то, как следует обращаться с вашим типом.
+1 к чистому коду ✅
Всем хороших выходных!
Всем привет!
Enum — это тип значения, определенный набором именованных констант и т.д. и т.п. далее по тексту из MSDN.
Это понятно. Есть вещи важнее понятных истин. Я говорю про дизайн. Вы знаете про хороший дизайн для Enum в C#?
Если нет, то скорее ловите, чтобы уже с понедельника ваш код стал выглядеть более профессионально.
Когда мы создаём новый Enum, мы всегда должны предоставлять значение по умолчанию (default value). В противном случае, первое значение в созданном Enum может быть рассмотрено как значение по умолчанию. Такое поведение нельзя считать верным и может вести к потенциально опасным и трудно отлаживаемым ошибкам.
Добавьте значение по умолчанию, которое будет адекватно вашему бизнес-домену. Это может быть "None", "Unknown" и прочее. Тем самым вы явно укажете то, как следует обращаться с вашим типом.
+1 к чистому коду ✅
Всем хороших выходных!
👍85🔥5❤3
Всем привет! 👻
Продолжаем разбираться с шаблонами проектирования "
Внимание❗️ Ролик может вызывать сильное чувство аппетита. Перекусите что-нибудь перед просмотром 😉
Приятного просмотра! 💙
https://www.youtube.com/watch?v=2ReKJaM2glI
Продолжаем разбираться с шаблонами проектирования "
банды четырёх (GoF)". Сегодня на повестке: Builder (Строитель). Строитель отделяет конструирование сложного объекта от его представления, так что в результате одного и того же процесса конструирования могут получаться разные представления.Внимание❗️ Ролик может вызывать сильное чувство аппетита. Перекусите что-нибудь перед просмотром 😉
Приятного просмотра! 💙
https://www.youtube.com/watch?v=2ReKJaM2glI
YouTube
C# Builder Строитель | Design Patterns
Паттерны проектирования важно и нужно знать. В этом ролике мы поговорим про design pattern Builder (Строитель) - порождающий шаблон проектирования. Поговорим о сути, посмотрим на примеры и сделаем реализацию в Visual Studio 2022 и .NET 6. Заходите, будет…
🔥44👍13❤2🎉2
#капитану_на_заметку
Всем привет!
В прошлой рубрике #капитану_на_заметку мы говорили про хороший дизайн для Enum. Сегодня мы затронем вопрос производительности использования Enum.
Получение строкового представления для перечисляемого типа — весьма частая операция. Это может потребоваться при формировании JSON, выводе данных в интерфейс Web или Desktop приложений, логировании в системы сбора логов и т. д. Для решения такой задачи, естественным действием разработчика на C# будет вызов метода ToString().
Если же значение перечисляемого типа известно заранее (на этапе компиляции), хорошей практикой является использование выражения
+1 к производительному коду ✅
Всем хороших выходных!
Всем привет!
В прошлой рубрике #капитану_на_заметку мы говорили про хороший дизайн для Enum. Сегодня мы затронем вопрос производительности использования Enum.
Получение строкового представления для перечисляемого типа — весьма частая операция. Это может потребоваться при формировании JSON, выводе данных в интерфейс Web или Desktop приложений, логировании в системы сбора логов и т. д. Для решения такой задачи, естественным действием разработчика на C# будет вызов метода ToString().
Если же значение перечисляемого типа известно заранее (на этапе компиляции), хорошей практикой является использование выражения
nameof. Значение выражения nameof вычисляется во время компиляции, и это не влияет на время выполнения. В ряде случаев, это может существенно повысить общую производительность вашего приложения. Поэтому, в следующий раз, перед вызовом ToString(), задумайтесь... может лучше nameof? 😉+1 к производительному коду ✅
Всем хороших выходных!
👍77🔥8😱1
#codaza_спрашивает
Всем привет! 👋
Сегодня у нас еще один занятный кусочек кода, которому стоит провести code review. Как обычно, всё компилируется и все тесты проходят на ура. Сомнений нет — код рабочий. Но всё же есть ощущение что в production-среду этому методу рановато. В production предстоит работа с большим массивом данных и, возможно, этот метод еще не готов к таким нагрузкам. А может и готов...
Есть идеи? 🤔
Всем привет! 👋
Сегодня у нас еще один занятный кусочек кода, которому стоит провести code review. Как обычно, всё компилируется и все тесты проходят на ура. Сомнений нет — код рабочий. Но всё же есть ощущение что в production-среду этому методу рановато. В production предстоит работа с большим массивом данных и, возможно, этот метод еще не готов к таким нагрузкам. А может и готов...
Есть идеи? 🤔
👍19🤔4
#codaza_отвечает
Что же... и в этот раз, коллективными усилиями, предложенный фрагмент кода, без преувеличения, был разобран по косточкам. В нашем сообществе не мало действительно профессиональных разработчиков. Если вы не успели пробежаться по комментариям в предыдущем посте, то прошу вас не полениться и сделать это, так как вы увидите развитие мысли и ссылки на дополнительную информацию от участников группы.
Где же был зарыт "главный слон" 🐘? Проблема, с которой данный код никак нельзя было допускать в production-среду. Проблема была на строках 12-13:
Далее, Александр увидел возможность для параллельной обработки данных в словаре dataSlices. При параллельной обработке нужно учитывать проблемы совместного доступа, поэтому, для словаря indexes, вместо Dictionary<int, int>, Александр взял потокобезопасную коллекцию ConcurrentDictionary<int, int>.
Метод ExtractStochasticDataAsync() является асинхронным и хорошей практикой является предоставления возможности для остановки метода (тем более метод работает в цикле с большим объемом данных). На эту проблему обратил внимание Aleksandr Radchenko, предложив использование CancellationToken.
Итоговый вариант после рефакторинга стал выглядеть так:
Всем активным участникам огромное спасибо за вклад! 💙
Что же... и в этот раз, коллективными усилиями, предложенный фрагмент кода, без преувеличения, был разобран по косточкам. В нашем сообществе не мало действительно профессиональных разработчиков. Если вы не успели пробежаться по комментариям в предыдущем посте, то прошу вас не полениться и сделать это, так как вы увидите развитие мысли и ссылки на дополнительную информацию от участников группы.
Где же был зарыт "главный слон" 🐘? Проблема, с которой данный код никак нельзя было допускать в production-среду. Проблема была на строках 12-13:
int keyWordIndex = dataSlice.Value.TextFraction.Substring(actualPosition).IndexOf(keyWord);Вызов метода Substring() - вот основная проблема. Дело в том, что при каждом вызове этого метода, в памяти будет создаваться полная копия строки, содержащаяся в свойстве TextFraction. Это не проблема при нечастных вызовах на небольших строках и без использования циклов. Для нашей ситуации это совершенно не годится, так как методу предстоит работать с большим массивом данных. Только представьте что может произойти с оперативной памятью при частых вызовах метода ExtractStochasticDataAsync(). И первым проблему заметил Александр Максименко, указав на то что вместо вызова Substring() следует делать вызов метода расширения для класса String - AsSpan(). Такой подход позволит не создавать копию строки TextFraction в оперативной памяти, что существенно снизит нагрузку на неё.
Далее, Александр увидел возможность для параллельной обработки данных в словаре dataSlices. При параллельной обработке нужно учитывать проблемы совместного доступа, поэтому, для словаря indexes, вместо Dictionary<int, int>, Александр взял потокобезопасную коллекцию ConcurrentDictionary<int, int>.
Метод ExtractStochasticDataAsync() является асинхронным и хорошей практикой является предоставления возможности для остановки метода (тем более метод работает в цикле с большим объемом данных). На эту проблему обратил внимание Aleksandr Radchenko, предложив использование CancellationToken.
Итоговый вариант после рефакторинга стал выглядеть так:
public async Task<IDictionary<int, int>> ExtractStochasticDataAsync(string keyWord, CancellationToken ct = default)Кроме того, Aleksandr Radchenko предложил еще ряд важных улучшений с примерами листингов (за что ему отдельное спасибо). Например, использовать в качестве возвращаемого значения метода ExtractStochasticDataAsync() асинхронную реализацию интерфейса IAsyncEnumerable и оператор yield, что позволит организовать асинхронную обработку данных из вызываемого метода. Это потребует изменение выходного API у метода ExtractStochasticDataAsync(), но если это возможно по имеющимся бизнес-требованиям, сделать это точно стоит. Этот вариант будет выглядеть так:
{
IDictionary<int, AnalyticalDataSlice> dataSlices = await LoadAnalyticalDataSlicesAsync(ct);
ConcurrentDictionary<int, int> indexes = new();
var tasks = dataSlices.Select(async ds =>
{
ct.ThrowIfCancellationRequested();
int actualPosition = await LoadActualPositionAsync(ct);
int keyWordIndex = ds.Value.TextFraction
.AsSpan(actualPosition).IndexOf(keyWord);
indexes.TryAdd(ds.Key, keyWordIndex + actualPosition);
});
await Task.WhenAll(tasks);
return indexes;
}
public async IAsyncEnumerable<(int Key, int Position)> ExtractStochasticDataAsync01(string keyWord, [EnumeratorCancellation] CancellationToken ct = default)Из таких code review можно получить гораздо больше ценных и практических знаний нежели из очередной статьи в интернете.
{
IDictionary<int, AnaliticalDataSlice> dataSlices = await LoadAnalyticalDataSlicesAsync(ct);
foreach(var dataSlice in dataSlices)
{
int actualPosition = await LoadActualPositionAsync(ct);
int keyWordIndex = dataSlice.Value.TextFraction
.AsSpan(actualPosition).IndexOf(keyWord);
ct.ThrowIfCancellationRequested();
yield return (dataSlice.Key, keyWordIndex + actualPosition);
};
}
Всем активным участникам огромное спасибо за вклад! 💙
👍67
#капитану_на_заметку
Всем привет!
В прошлый раз мы поговорили об использовании метода расширения AsSpan(), который можно вызывать на объектах класса String и предотвратить ненужные аллокации в оперативной памяти. В продолжении темы, мне хотелось бы рассказать про похожую возможность, но уже для коллекции List.
Возникают ситуации, когда нам нужно получить некоторый диапазон значений из List. Как поступал разработчик на C# до появления .NET5? Очень просто, он вызывал метод GetRange() и получал желаемый диапазон значений. Такое действие неминуемо сопровождается выделениями в оперативной памяти. Сегодня мы можем этого избежать и значительно снизить нагрузку на оперативную память воспользовавшись статическим методом AsSpan(), который располагается в классе CollectionsMarshal. Этот метод вернёт нам Span<T> представление данных в списке, после чего можно вызвать метод Slice() c такими же параметрами, как у GetRange().
+1 к производительному коду ✅
Всем хороших выходных!
Всем привет!
В прошлый раз мы поговорили об использовании метода расширения AsSpan(), который можно вызывать на объектах класса String и предотвратить ненужные аллокации в оперативной памяти. В продолжении темы, мне хотелось бы рассказать про похожую возможность, но уже для коллекции List.
Возникают ситуации, когда нам нужно получить некоторый диапазон значений из List. Как поступал разработчик на C# до появления .NET5? Очень просто, он вызывал метод GetRange() и получал желаемый диапазон значений. Такое действие неминуемо сопровождается выделениями в оперативной памяти. Сегодня мы можем этого избежать и значительно снизить нагрузку на оперативную память воспользовавшись статическим методом AsSpan(), который располагается в классе CollectionsMarshal. Этот метод вернёт нам Span<T> представление данных в списке, после чего можно вызвать метод Slice() c такими же параметрами, как у GetRange().
+1 к производительному коду ✅
Всем хороших выходных!
👍77🔥3❤1👎1
#капитану_на_заметку
Я никогда не делаю исключений. Исключения опровергают правило.
Артур Конан Дойл
Всем привет!
Тема правильной работы с исключениями (exceptions) весьма холиварна и едва ли подлежит окончательному разрешению 🙂 Но сегодня я хочу обратить вниманием на другое — на цену. Цену использования исключений. Работаете вы с ними правильно или неправильно, держите в голове что за это придётся заплатить цену. При работе с большими массивами данных это может привести к значительной деградации производительности вашего приложения. Я не говорю о том, что мы не должны генерировать исключения, но разработчик обязан понимать, что выбрасывать исключения без аргументированной необходимости — плохая идея.
Руководствуйтесь простым правилом: Исключения для исключительных ситуаций.
Вы знаете как обработать исключение? Просто сделайте это без лишних throw 😉
+1 к исключительному коду ✅
Всем хороших выходных!
Я никогда не делаю исключений. Исключения опровергают правило.
Артур Конан Дойл
Всем привет!
Тема правильной работы с исключениями (exceptions) весьма холиварна и едва ли подлежит окончательному разрешению 🙂 Но сегодня я хочу обратить вниманием на другое — на цену. Цену использования исключений. Работаете вы с ними правильно или неправильно, держите в голове что за это придётся заплатить цену. При работе с большими массивами данных это может привести к значительной деградации производительности вашего приложения. Я не говорю о том, что мы не должны генерировать исключения, но разработчик обязан понимать, что выбрасывать исключения без аргументированной необходимости — плохая идея.
Руководствуйтесь простым правилом: Исключения для исключительных ситуаций.
Вы знаете как обработать исключение? Просто сделайте это без лишних throw 😉
+1 к исключительному коду ✅
Всем хороших выходных!
👍48🔥6
#капитану_на_заметку
Всем привет!
Спешу поделиться с вами интересным практическим случаем работы со строками. Недавно мне довелось работать с одним из австрийских сервисов обработки сообщений из различных мессенджеров (WhatsApp, Viber и пр). Ребята собирают аналитическую статистику по отправляемым сообщениям и столкнулись с тем, что данные в этой статистике, время от времени, искажались непонятным образом.
Я проанализировал исходный код сервиса и обнаружил, что проблема в подсчёте длины строки сообщений. Разработчик сервиса воспользовался свойством Length у экземпляра класса String. Это решение было логично и казалось единственно верным. Проблема в том, что люди часто используют Emoji в своих сообщениях и 1 символ Emoji не равен длине одного символа, так как для их кодировки используется Unicode.
Для правильного сбора статистки требовалась именно посимвольная длина строки. Свойство LengthInTextElements (класс StringInfo) решает именно эту задачу.
+1 к опыту работу со строками ✅
Всем хороших выходных!
Всем привет!
Спешу поделиться с вами интересным практическим случаем работы со строками. Недавно мне довелось работать с одним из австрийских сервисов обработки сообщений из различных мессенджеров (WhatsApp, Viber и пр). Ребята собирают аналитическую статистику по отправляемым сообщениям и столкнулись с тем, что данные в этой статистике, время от времени, искажались непонятным образом.
Я проанализировал исходный код сервиса и обнаружил, что проблема в подсчёте длины строки сообщений. Разработчик сервиса воспользовался свойством Length у экземпляра класса String. Это решение было логично и казалось единственно верным. Проблема в том, что люди часто используют Emoji в своих сообщениях и 1 символ Emoji не равен длине одного символа, так как для их кодировки используется Unicode.
Для правильного сбора статистки требовалась именно посимвольная длина строки. Свойство LengthInTextElements (класс StringInfo) решает именно эту задачу.
+1 к опыту работу со строками ✅
Всем хороших выходных!
👍160🔥22👏3