День восемьсот двадцать четвёртый. #ЗаметкиНаПолях
Как Обратиться к HttpContext в ASP.NET Core
Как правило, переход от веб-форм или из MVC5 в ASP.NET Core требует значительного рефакторинга. Например,
Что такое HttpContext?
Это объект, содержащий информацию о текущем Http-запросе: авторизация, аутентификация, объекты запроса и ответа, сессию, текущего пользователя, отправленные данные формы и т.п. Каждый Http-запрос создаёт новый объект
1. Доступ к HttpContext из контроллера?
Контроллеры предоставляют свойство
Если вы пишете собственный вариант промежуточного ПО, то контекст текущего запроса передаётся в метод Invoke/InvokeAsync:
Если нужно получить доступ к
Шаг 1. Зарегистрируйте зависимость и сервис:
- https://www.telerik.com/blogs/how-to-get-httpcontext-asp-net-core
- https://stackoverflow.com/questions/38571032/how-to-get-httpcontext-current-in-asp-net-core
Как Обратиться к HttpContext в ASP.NET Core
Как правило, переход от веб-форм или из MVC5 в ASP.NET Core требует значительного рефакторинга. Например,
HttpContext.Current
был удалён в ASP.NET Core. Доступ к текущему контексту HTTP из отдельной библиотеки классов – это один из примеров беспорядочной архитектуры, которых ASP.NET Core пытается избежать. Есть несколько способов получить текущий контекст запроса в ASP.NET Core.Что такое HttpContext?
Это объект, содержащий информацию о текущем Http-запросе: авторизация, аутентификация, объекты запроса и ответа, сессию, текущего пользователя, отправленные данные формы и т.п. Каждый Http-запрос создаёт новый объект
HttpContext
с актуальной информацией.1. Доступ к HttpContext из контроллера?
Контроллеры предоставляют свойство
ControllerBase.HttpContext
, чтобы мы могли напрямую обращаться к контексту текущего Http-запроса:[HttpGet("/getDetails")]Вывод:
public string GetDetails() {
var result =
"Method: " + HttpContext.Request.iss.onethod +
" Path: " + HttpContext.Request.Path;
return result;
}
Method: GET Path: /getdetails2. Доступ к HttpContext из промежуточного ПО
Если вы пишете собственный вариант промежуточного ПО, то контекст текущего запроса передаётся в метод Invoke/InvokeAsync:
public async Task InvokeAsync (HttpContext context) {3. Доступ к HttpContext из сервиса
…
}
Если нужно получить доступ к
HttpContext
в сервисе, мы можем сделать это с помощью интерфейса IHttpContextAccessor
и его реализации по умолчанию HttpContextAccessor
. Эту зависимость необходимо добавлять только в том случае, если мы хотим получить доступ к HttpContext
в сервисе.Шаг 1. Зарегистрируйте зависимость и сервис:
public void ConfigureServicesШаг 2: внедрите
(IServiceCollection services) {
…
services.AddHttpContextAccessor();
// до .NET Core 2.2 используйте
//services
// .TryAddSingleton<IHttpContextAccessor,
// HttpContextAccessor>();
services.AddTransient<IUserService, UserService>();
…
}
IHttpContextAccessor
в конструктор сервиса:public class UserService : IUserService {Источники:
private IHttpContextAccessor
_httpContextAccessor;
public UserService(IHttpContextAccessor hca)
{
_httpContextAccessor = hca;
}
public string GetLoginUserName() {
return _httpContextAccessor
.HttpContext.User.Identity.Name;
}
}
- https://www.telerik.com/blogs/how-to-get-httpcontext-asp-net-core
- https://stackoverflow.com/questions/38571032/how-to-get-httpcontext-current-in-asp-net-core
👍1
День восемьсот двадцать пятый. #DeveloperPath
Новости проекта «Путь Разработчика»
О проекте
Очередная серия новостей проекта.
1. Релиз наладили, даже кое-какое CI/CD прикрутили.
2. Проект выложен, и даже получил домен https://www.developer-path.com/
Пока там ничего особо нет, только краткое описание и форма входа. Постепенно будем пилить UI. Так что, если есть веб-дизайнеры, желающие присоединиться, нарисовать лого, айдентику, интерфейс, вот это всё, будем очень признательны.
3. API тоже доступно публично, пока тут https://developerpathapi.azurewebsites.net/index.html
Пожалуйста, не ломайте ничего :)
Как уже писал раньше, задачи найдутся для самых разных специалистов. Если кто-то хочет поковыряться в .NET 5, C#9, Blazor, паттерне CQRS или веб-дизайне, милости просим.
Проект всё также на Azure DevOps: https://dev.azure.com/sbenzenko/DeveloperPath/
Он открытый, поэтому посмотреть, почитать можно и без регистрации. Авторизованные через аккаунт Microsoft или GitHub могут оставлять комментарии или добавиться в команду. Если вы ещё не в команде, пишите в личку или в комментарии свой e-mail, я вас приглашу.
Задачи можно посмотреть в Azure DevOps.
Помимо этого, вы, конечно, можете просто взять код проекта на GitHub и предложить что-то своё.
По всем вопросам добро пожаловать в комментарии сюда, либо в проект на Azure DevOps.
Новости проекта «Путь Разработчика»
О проекте
Очередная серия новостей проекта.
1. Релиз наладили, даже кое-какое CI/CD прикрутили.
2. Проект выложен, и даже получил домен https://www.developer-path.com/
Пока там ничего особо нет, только краткое описание и форма входа. Постепенно будем пилить UI. Так что, если есть веб-дизайнеры, желающие присоединиться, нарисовать лого, айдентику, интерфейс, вот это всё, будем очень признательны.
3. API тоже доступно публично, пока тут https://developerpathapi.azurewebsites.net/index.html
Пожалуйста, не ломайте ничего :)
Как уже писал раньше, задачи найдутся для самых разных специалистов. Если кто-то хочет поковыряться в .NET 5, C#9, Blazor, паттерне CQRS или веб-дизайне, милости просим.
Проект всё также на Azure DevOps: https://dev.azure.com/sbenzenko/DeveloperPath/
Он открытый, поэтому посмотреть, почитать можно и без регистрации. Авторизованные через аккаунт Microsoft или GitHub могут оставлять комментарии или добавиться в команду. Если вы ещё не в команде, пишите в личку или в комментарии свой e-mail, я вас приглашу.
Задачи можно посмотреть в Azure DevOps.
Помимо этого, вы, конечно, можете просто взять код проекта на GitHub и предложить что-то своё.
По всем вопросам добро пожаловать в комментарии сюда, либо в проект на Azure DevOps.
День восемьсот двадцать шестой. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 9-12
Начало 1-4
Продолжение 5-8
9. Что обязательно будет содержать дамп памяти любого приложения .NET?
Забавный факт, который иногда может вводить в заблуждение при анализе дампов памяти: каждый процесс .NET заведомо выделяет экземпляры
Кроме того, некоторые массивы
10. Что означает выражение "stop the world" в контексте сборки мусора?
Каждый запуск сборки мусора в .NET имеет несколько коротких и длинных фаз, когда ожидается, что среда выполнения «приостановит» приложение. Эти паузы также известны как фазы «остановки мира» ("stop the world"). И в целом они явно нежелательны, так как влияют на скорость отклика и производительность приложения. Многие улучшения .NET GC делают эти паузы как можно короче.
Итак, «остановка мира» - это пауза на время выполнения сборки мусора (или её части), когда все управляемые потоки «приостановлены». Например, это позволяет получить единообразное представление о том, какие управляемые объекты используются. Неуправляемые потоки не приостанавливаются, поскольку в этом нет необходимости - по умолчанию они не обращаются к управляемым объектам, и даже если это так, они обращаются к «закреплённым» (pinned) объектам, которые обрабатываются особым образом.
11. Как работает параллельная сборка мусора в .NET 5?
Параллельная (фоновая) сборка мусора большую часть времени работает одновременно с управляемыми потоками, не создавая длительных пауз для «остановки мира». Это не означает, что пауз вообще не бывает. Требуется как минимум две паузы: в начале и где-то посередине, чтобы получить некоторое согласованное представление о состоянии памяти.
Более того, текущая реализация .NET GC не может сжимать память во время работы приложения. Таким образом, как полная сборка мусора, так и сборка в поколениях 0 и 1, должны работать в режиме «остановки мира», если требуется сжать память.
Работа над внедрением параллельного сжатия памяти определённо продолжается, и рано или поздно мы можем ожидать его добавления в экосистему .NET. Такие реализации успешно используются, например, в экосистеме JVM. Тем не менее, у них есть свои недостатки и компромиссы, поскольку ничего не даётся бесплатно.
12. Как параметры нужно учитывать, чтобы оценить потребление памяти вашей программой?
Чтобы иметь хорошее общее представление о потреблении памяти вашей .NET-программой, лучше всего знать как минимум три величины.
1. Сколько памяти выделено вашей программе в целом (частные байты - private bytes).
2. Сколько её физически потребляется в RAM («рабочий набор» - working set) - это позволит заметить случаи, когда страницы памяти выгружаются на диск.
3. В контексте .NET полезно знать, сколько памяти потребляется в управляемой куче (Managed Heap).
Что же касается виртуального адресного пространства (virtual address space), оно охватывает всё адресное пространство, которое может использоваться нашим приложением, и не связано напрямую с поведением приложения.
Продолжение следует…
Источник: https://dotnetmemoryexpert.com
Топ Вопросов о Памяти в .NET. Продолжение 9-12
Начало 1-4
Продолжение 5-8
9. Что обязательно будет содержать дамп памяти любого приложения .NET?
Забавный факт, который иногда может вводить в заблуждение при анализе дампов памяти: каждый процесс .NET заведомо выделяет экземпляры
OutOfMemoryException
и StackOverflowException
. Просто на случай, если они понадобятся, что будет означать действительную нехватку памяти и отсутствие возможности их создать!Кроме того, некоторые массивы
object[]
предварительно выделяются в LOH для ссылок на объекты, используемые «дескрипторами». Это важный механизм, используемый интернированием строк, статикой и локальной статикой потока. Так, например, даже если вы вообще не используете LOH, вы увидите там несколько массивов, ссылающихся на ваши статические объекты.10. Что означает выражение "stop the world" в контексте сборки мусора?
Каждый запуск сборки мусора в .NET имеет несколько коротких и длинных фаз, когда ожидается, что среда выполнения «приостановит» приложение. Эти паузы также известны как фазы «остановки мира» ("stop the world"). И в целом они явно нежелательны, так как влияют на скорость отклика и производительность приложения. Многие улучшения .NET GC делают эти паузы как можно короче.
Итак, «остановка мира» - это пауза на время выполнения сборки мусора (или её части), когда все управляемые потоки «приостановлены». Например, это позволяет получить единообразное представление о том, какие управляемые объекты используются. Неуправляемые потоки не приостанавливаются, поскольку в этом нет необходимости - по умолчанию они не обращаются к управляемым объектам, и даже если это так, они обращаются к «закреплённым» (pinned) объектам, которые обрабатываются особым образом.
11. Как работает параллельная сборка мусора в .NET 5?
Параллельная (фоновая) сборка мусора большую часть времени работает одновременно с управляемыми потоками, не создавая длительных пауз для «остановки мира». Это не означает, что пауз вообще не бывает. Требуется как минимум две паузы: в начале и где-то посередине, чтобы получить некоторое согласованное представление о состоянии памяти.
Более того, текущая реализация .NET GC не может сжимать память во время работы приложения. Таким образом, как полная сборка мусора, так и сборка в поколениях 0 и 1, должны работать в режиме «остановки мира», если требуется сжать память.
Работа над внедрением параллельного сжатия памяти определённо продолжается, и рано или поздно мы можем ожидать его добавления в экосистему .NET. Такие реализации успешно используются, например, в экосистеме JVM. Тем не менее, у них есть свои недостатки и компромиссы, поскольку ничего не даётся бесплатно.
12. Как параметры нужно учитывать, чтобы оценить потребление памяти вашей программой?
Чтобы иметь хорошее общее представление о потреблении памяти вашей .NET-программой, лучше всего знать как минимум три величины.
1. Сколько памяти выделено вашей программе в целом (частные байты - private bytes).
2. Сколько её физически потребляется в RAM («рабочий набор» - working set) - это позволит заметить случаи, когда страницы памяти выгружаются на диск.
3. В контексте .NET полезно знать, сколько памяти потребляется в управляемой куче (Managed Heap).
Что же касается виртуального адресного пространства (virtual address space), оно охватывает всё адресное пространство, которое может использоваться нашим приложением, и не связано напрямую с поведением приложения.
Продолжение следует…
Источник: https://dotnetmemoryexpert.com
День восемьсот двадцать седьмой. #ЧтоНовенького
.NET 6: Улучшения в LINQ. Начало 1/2
Продолжаем изучать, что нас ждёт в релизе .NET 6. На этот раз про LINQ.
Индексные операции в IEnumerable<T>
Первоначально
С появлением LINQ многие из этих предположений позабылись. Методы расширения, такие как
Когда на
Для больших коллекций такой подсчет может быть очень дорогим, особенно для
Трёхсторонний Zip
Метод расширения
Разбиение на партии
Часто требуется разбить коллекцию на несколько партий. Например, может оказаться, что вставка 100 строк в базу за раз быстрее, чем вставка по одной или всех сразу. Хотя этот код нетрудно написать самому, он, как правило, подвержен ошибкам. Легко ошибиться в последней партии, если количество строк не делится равномерно на размер партии. Для этого добавлен метод расширения
Когда сам API не может предотвратить неправильное использование кода разработчиками, авторы библиотек все чаще обращаются к анализаторам. Некоторые из них встроены в компилятор C#, другие добавляются через библиотеки, такие как NetAnalyzers и Roslynator.
Первый из новых анализаторов касается метода расширения
Предложение правильного использования AsParallel() относится к случаям, когда
Окончание следует…
Источник: https://www.infoq.com/news/2021/04/Net6-Linq/
.NET 6: Улучшения в LINQ. Начало 1/2
Продолжаем изучать, что нас ждёт в релизе .NET 6. На этот раз про LINQ.
Индексные операции в IEnumerable<T>
Первоначально
IList<T>
отличался от IEnumerable<T>
наличием обращения по индексу. Идея заключалась в том, что только коллекции, поддерживающие быстрые операции с индексами ~O(1), могут реализовывать IList<T>
. Теоретически индексные операции над IEnumerable<T>
не поощрялись, поскольку считались медленными.С появлением LINQ многие из этих предположений позабылись. Методы расширения, такие как
Enumerable.Count()
и Enumerable.ElementAt()
доступны на любой коллекций, даже если в реальности они выполняются за O(N) время. Новые методы расширения для IEnumerable
(ElementAt
, ElementAtOrDefault
и Take
) продолжают этот тренд, позволяя использовать индексы и диапазоны:var elements = source.Take(range: 10..^10);Операции подсчёта в IEnumerable<T>
Когда на
IEnumerable<T>
вызывается Count()
, происходят две вещи. Сначала библиотека LINQ пытается привести коллекцию к интерфейсу, который предоставляет свойство Count
. Если это невозможно, метод выполняет итерацию по всей коллекции, считая элементы.Для больших коллекций такой подсчет может быть очень дорогим, особенно для
IQueryable
при запросе к базе данных. Поэтому разработчики попросили «безопасную» функцию подсчета. Эта функция проверяет наличие быстрого свойства Count
и, если не может его найти, ничего не возвращает:public static bool TryGetNonEnumeratedCount(this IEnumerable<T> source, out int count);Длинное имя
TryGetNonEnumeratedCount
как бы намекает, что вы делаете что-то не то. В идеале любой API, возвращающий список, должен возвращать либо коллекцию со строгим именем, либо интерфейс более высокого уровня, такой как IList<T>
или IReadOnlyList<T>
.Трёхсторонний Zip
Метод расширения
Zip
объединяет две коллекции, перечисляя их одновременно. Например, если у вас есть список 1, 2, 3
и список A, B, C
, то результирующая коллекция будет состоять из кортежей (1, A), (2, B), (3, C)
. Теперь предложено расширить метод для объединения трёх коллекций за раз.Разбиение на партии
Часто требуется разбить коллекцию на несколько партий. Например, может оказаться, что вставка 100 строк в базу за раз быстрее, чем вставка по одной или всех сразу. Хотя этот код нетрудно написать самому, он, как правило, подвержен ошибкам. Легко ошибиться в последней партии, если количество строк не делится равномерно на размер партии. Для этого добавлен метод расширения
Chunk
для IEnumerable
и IQueryable
:public static IEnumerable<T[]> Chunk(this IEnumerable<T> source, int size);Проверки анализатора
public static IQueryable<T[]> Chunk(this IQueryable<T> source, int size);
Когда сам API не может предотвратить неправильное использование кода разработчиками, авторы библиотек все чаще обращаются к анализаторам. Некоторые из них встроены в компилятор C#, другие добавляются через библиотеки, такие как NetAnalyzers и Roslynator.
Первый из новых анализаторов касается метода расширения
OfType<T>
. Он фильтрует входную коллекцию, возвращая только элементы указанного типа T
. Если входной тип не может быть приведен к выходному типу, текущее поведение - просто вернуть пустую коллекцию. Предложено в этом случае выдавать предупреждение компилятора, чтобы избежать излишнего перебора коллекции.Предложение правильного использования AsParallel() относится к случаям, когда
AsParallel()
следует за операцией перечисления коллекции. Хотя это неочевидно, AsParallel()
должен использоваться перед любыми операциями, которые можно распараллелить, такими как отображение и фильтрация. В этом случае также будет выдано предупреждение компилятора.Окончание следует…
Источник: https://www.infoq.com/news/2021/04/Net6-Linq/
День восемьсот двадцать восьмой. #ЧтоНовенького
.NET 6: Улучшения в LINQ. Окончание 2/2
Начало
Операторы *By
Это методы
В
Сигнатуры всех методов похожи, приведу для примера
Вариант оператора *OrDefault используется для предоставления значения по умолчанию, когда
Источник: https://www.infoq.com/news/2021/04/Net6-Linq/
.NET 6: Улучшения в LINQ. Окончание 2/2
Начало
Операторы *By
Это методы
DistinctBy
, ExceptBy
, IntersectBy
, UnionBy
¸ MinBy
и MaxBy
. Первые 4 позволяют работать с коллекциями как со множествами. Для них предусмотрен делегат keySelector
, возвращающий ключ для сравнения. Методы позволят выполнять операцию сравнения на подмножестве данных, а не на всей коллекции. Их можно использовать для повышения производительности или для обеспечения настраиваемого поведения без потери исходных данных. Например, имея коллекции всех заказов и завершённых заказов:IEnumerable<Order> all = GetAll();Получить незавершённые заказы можно с помощью
IEnumerable<Order> completed = GetCompleted();
ExceptBy
, сравнивая элементы по свойству ID:IEnumerable<Order> remaining =В отличие от варианта с использованием
all.ExceptBy(completed, o => o.ID);
Where
, который менее интуитивно понятен и выполняется за квадратичное время (O(M*N)).В
MinBy
и MaxBy
вместо селектора ключей предоставляется селектор элементов. Они вернут элемент коллекции с наибольшим/наименьшим значением соответственно, в отличие от существующих Min
и Max
, которые возвращают само значение. Кроме того, в каждый из методов (в том числе и Min
и Max
) можно будет передать необязательный объект-компаратор.Сигнатуры всех методов похожи, приведу для примера
DistinctBy
и MinBy
с компаратором:public static IEnumerable<TSource>Улучшение в *OrDefault
DistinctBy<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector);
public static TSource
MinBy<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector,
IComparer<TResult>? comparer);
Вариант оператора *OrDefault используется для предоставления значения по умолчанию, когда
Single
, First
или Last
передаётся пустая коллекция. В этой функции теперь можно будет переопределить возвращаемое значение по умолчанию: public static TSourceПримечание: обратите внимание, что во всех описанных выше методах расширения есть как версия
SingleOrDefault<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate,
TSource defaultValue);
IEnumerable
, так и версия IQueryable
. Все новые API, возвращающие IEnumerable
, должны включать соответствующую версию IQueryable
. Это не позволит пользователю API преобразовать запрос в обычное перечисление, не осознавая этого.Источник: https://www.infoq.com/news/2021/04/Net6-Linq/
День восемьсот двадцать девятый. #Testing
Несколько хороши ваши тесты? Протестируйте их с помощью мутатора Stryker
Вы взялись за новый проект и решили всё сделать правильно. Возможно даже разрабатывать через TDD. И вот, всё готово, все тесты зелёные, покрытие тестами полное, вы гордитесь своей работой. Затем от клиента приходит запрос на новый функционал. Вы что-то добавляете, также решаете что-то отрефакторить, т.к. лучше поняли домен и требования клиента, добавляете новые тесты, все тесты (и старые, и новые) проходят, всё прекрасно.
И тут приходит сообщение от клиента… В смысле «не работает»???
Внезапно вы находите ошибку, и не в новом функционале, а в исходной версии кода. Но как же так? Все тесты проходили. Оказывается, вы не охватили тестами все случаи, несмотря на 100% покрытие. Затем вы изменили кое-что здесь и там, и в результате функциональность уже не работает должным образом. Как снизить вероятность повторения такой ситуации?
Что такое мутационное тестирование?
Мутационное тестирование - это введение ошибок (мутантов) в ваш производственный код. Например, равенство заменяется неравенством, AND на OR и т.п. Затем выполняются тесты для каждого мутанта, и тесты должны провалиться! Неудача означает, что мутант убит. Если тесты проходят, мутант выжил, а значит, есть ложноположительные результаты.
Stryker делает всё это автоматически. Он поддерживает различные мутаторы, например арифметические операторы, равенство, логические операторы, мутации строк и даже LINQ. Вы можете посмотреть полный список доступных мутаторов в документации.
Stryker устанавливается как d
Отдельный интересный пример – замена LINQ методов, например,
Итого
Вы можете спросить: когда следует запускать мутационные тесты? В CI/CD, локально или время от времени?
Пока не понятно. Я всё ещё новичок в мутационном тестировании. На данный момент думаю, имеет смысл добавить его как часть разработки. То есть, начиная работу с некоторым кодом, запустить Stryker, чтобы посмотреть хорошо ли код покрыт тестами. Если да, можно уверенно работать с этим кодом. Если нет - то сначала сосредоточьтесь на увеличении охвата, а затем на развитии кода. Сделать Stryker частью вашего CI/CD также возможно.
Stryker и мутационное тестирование в целом - не серебряная пуля. Я бы скорее назвал его страховкой. Закончили кодирование функции или исправление ошибки? Сделайте одолжение себе и своей команде и проверьте, охватывают ли ваши тесты все возможные случаи. Да? Отлично. Нет? Просто исправьте это.
Источник: https://lukaszcoding.com/how-good-are-your-net-tests-test-your-tests-with-stryker-mutator/
Несколько хороши ваши тесты? Протестируйте их с помощью мутатора Stryker
Вы взялись за новый проект и решили всё сделать правильно. Возможно даже разрабатывать через TDD. И вот, всё готово, все тесты зелёные, покрытие тестами полное, вы гордитесь своей работой. Затем от клиента приходит запрос на новый функционал. Вы что-то добавляете, также решаете что-то отрефакторить, т.к. лучше поняли домен и требования клиента, добавляете новые тесты, все тесты (и старые, и новые) проходят, всё прекрасно.
И тут приходит сообщение от клиента… В смысле «не работает»???
Внезапно вы находите ошибку, и не в новом функционале, а в исходной версии кода. Но как же так? Все тесты проходили. Оказывается, вы не охватили тестами все случаи, несмотря на 100% покрытие. Затем вы изменили кое-что здесь и там, и в результате функциональность уже не работает должным образом. Как снизить вероятность повторения такой ситуации?
Что такое мутационное тестирование?
Мутационное тестирование - это введение ошибок (мутантов) в ваш производственный код. Например, равенство заменяется неравенством, AND на OR и т.п. Затем выполняются тесты для каждого мутанта, и тесты должны провалиться! Неудача означает, что мутант убит. Если тесты проходят, мутант выжил, а значит, есть ложноположительные результаты.
Stryker делает всё это автоматически. Он поддерживает различные мутаторы, например арифметические операторы, равенство, логические операторы, мутации строк и даже LINQ. Вы можете посмотреть полный список доступных мутаторов в документации.
Stryker устанавливается как d
otnet tool
глобально или для каждого проекта в отдельности. dotnet tool install dotnet-stryker
Затем он запускается из папки юнит-тестовdotnet stryker
и создаёт HTML отчёт с результатами мутационного тестирования (см. в верхней части рисунка ниже). Как видите, только 3 «мутанта» были убиты, а 23 выжили, и мы получаем результат мутационных тестов всего в 11,54%. Это значит, что нам нужно добавить юнит-тестов в систему и покрыть тестами случаи, когда мутанты выживают. Можно «провалиться» в каждую папку и каждый отдельный файл из отчёта, и посмотреть, какие мутационные тесты были выполнены, и их результат. Например, в нижней части рисунка мы видим «выжившего мутанта». То есть, у нас нет теста, проверяющего случай, когда _orderStatusId
не равен OrderStatus.Submitted.Id,
и нам нужно его добавить.Отдельный интересный пример – замена LINQ методов, например,
First/Single
на FirstOrDefault/SingleOrDefault
и наоборот. Здесь проверяется случайное неверное использование. В случае пустой коллекции First/Single
должны выбрасывать исключение, тогда как *OrDefault
версии нет.Итого
Вы можете спросить: когда следует запускать мутационные тесты? В CI/CD, локально или время от времени?
Пока не понятно. Я всё ещё новичок в мутационном тестировании. На данный момент думаю, имеет смысл добавить его как часть разработки. То есть, начиная работу с некоторым кодом, запустить Stryker, чтобы посмотреть хорошо ли код покрыт тестами. Если да, можно уверенно работать с этим кодом. Если нет - то сначала сосредоточьтесь на увеличении охвата, а затем на развитии кода. Сделать Stryker частью вашего CI/CD также возможно.
Stryker и мутационное тестирование в целом - не серебряная пуля. Я бы скорее назвал его страховкой. Закончили кодирование функции или исправление ошибки? Сделайте одолжение себе и своей команде и проверьте, охватывают ли ваши тесты все возможные случаи. Да? Отлично. Нет? Просто исправьте это.
Источник: https://lukaszcoding.com/how-good-are-your-net-tests-test-your-tests-with-stryker-mutator/
👍2
День восемьсот тридцатый. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 13-16
Начало 1-4
Продолжение 5-8
Продолжение 9-12
13. Что такое POH?
Куча закреплённых объектов (Pinned Object Heap) - это новый тип раздела управляемой кучи (помимо SOH и LOH), добавленный в .NET 5 и предназначенный для объектов, которые не будут перемещаться сборщиком мусора. Таким образом, POH никогда не уплотняется, что позволяет рассматривать все объекты в POH как «закреплённые» по умолчанию. Это полезно, потому что закрепление в SOH/LOH вводит некоторые накладные расходы и снижает гибкость сжатия памяти (что может привести к фрагментации). Наличие специального места для «закреплённых» объектов устраняет эту проблему.
Однако для аллокаций памяти был добавлен новый API, потому что теперь нам нужно указать, что мы выделяем объект, который необходимо немедленно закрепить. В настоящее время это можно сделать с помощью
14. Какую сборку мусора мы можем вызвать через API GC?
В .NET доступны API, позволяющие контролировать поведение сборщика мусора. Например,
С другой стороны, действительно есть возможность запустить сжатие LOH при следующей непараллельной полной сборке мусора вот таким способом:
Когда мы запускаем приложение .NET, оно выполняется средой выполнения .NET внутри обособленного процесса. Другими словами, каждое приложение .NET выполняется своим собственным экземпляром среды выполнения, и между ними нет ничего общего. Это позволяет нам настраивать и управлять жизненным циклом каждого приложения .NET отдельно. Это касается и сборщика мусора, и управляемой кучи. Каждый процесс имеет свой собственные кучу и сборщик мусора и не знает о существовании других. Сборщик мусора, запускаемый в одном приложении .NET, не влияет (и не запускает) сборщик мусора в другом приложении .NET.
При этом процессы, запущенные на одном компьютере, совместно используют некоторые ресурсы, такие как ЦП, память и диски. Очевидно, что на этом уровне может произойти некоторое совместное использование ресурсов.
16. Каково предназначение флага gcAllowVeryLargeObjects?
Если вы когда-либо достигали предела общего размера объекта в 2Гб - возможно, массива или строки, хорошей новостью для вас является то, что вы можете преодолеть это ограничение с помощью флага
Продолжение следует…
Источник: https://dotnetmemoryexpert.com
Топ Вопросов о Памяти в .NET. Продолжение 13-16
Начало 1-4
Продолжение 5-8
Продолжение 9-12
13. Что такое POH?
Куча закреплённых объектов (Pinned Object Heap) - это новый тип раздела управляемой кучи (помимо SOH и LOH), добавленный в .NET 5 и предназначенный для объектов, которые не будут перемещаться сборщиком мусора. Таким образом, POH никогда не уплотняется, что позволяет рассматривать все объекты в POH как «закреплённые» по умолчанию. Это полезно, потому что закрепление в SOH/LOH вводит некоторые накладные расходы и снижает гибкость сжатия памяти (что может привести к фрагментации). Наличие специального места для «закреплённых» объектов устраняет эту проблему.
Однако для аллокаций памяти был добавлен новый API, потому что теперь нам нужно указать, что мы выделяем объект, который необходимо немедленно закрепить. В настоящее время это можно сделать с помощью
GC.AllocateUninitializedArray.
То есть мы можем выделить там только массив типов, которые не являются ссылочными и не содержат ссылок (например, массив байтов). Хотя собственно POH не имеет таких ограничений.14. Какую сборку мусора мы можем вызвать через API GC?
В .NET доступны API, позволяющие контролировать поведение сборщика мусора. Например,
GC.Collect
и его перегрузки позволяют нам запускать сборку мусора в поколениях 0, 1 или полную (с указанием, хотите ли вы, чтобы она была блокирующей и/или с уплотнением памяти). Однако из-за того, как работает сборка мусора в .NET, мы не можем запустить сборку только поколения 2 или только LOH. При сборке поколения (или LOH) всегда выполняется сборка мусора для всех младших поколений. Поэтому, когда мы запускаем сборку в поколении 2, мы запускаем полную сборку мусора. И нет API для запуска только сборки в LOH (в этом случае также будет запущена полная сборка).С другой стороны, действительно есть возможность запустить сжатие LOH при следующей непараллельной полной сборке мусора вот таким способом:
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;15. Используются ли память и сборщик мусора разными .NET приложениями совместно?
GC.Collect();
Когда мы запускаем приложение .NET, оно выполняется средой выполнения .NET внутри обособленного процесса. Другими словами, каждое приложение .NET выполняется своим собственным экземпляром среды выполнения, и между ними нет ничего общего. Это позволяет нам настраивать и управлять жизненным циклом каждого приложения .NET отдельно. Это касается и сборщика мусора, и управляемой кучи. Каждый процесс имеет свой собственные кучу и сборщик мусора и не знает о существовании других. Сборщик мусора, запускаемый в одном приложении .NET, не влияет (и не запускает) сборщик мусора в другом приложении .NET.
При этом процессы, запущенные на одном компьютере, совместно используют некоторые ресурсы, такие как ЦП, память и диски. Очевидно, что на этом уровне может произойти некоторое совместное использование ресурсов.
16. Каково предназначение флага gcAllowVeryLargeObjects?
Если вы когда-либо достигали предела общего размера объекта в 2Гб - возможно, массива или строки, хорошей новостью для вас является то, что вы можете преодолеть это ограничение с помощью флага
gcAllowVeryLargeObjects
(по умолчанию отключен). Он позволяет создавать массивы размером более 2Гб, но применим только к массивам (то есть не применим к строкам или другим типам объектов) и только к общему размеру массива. Максимальное количество элементов в массиве (UInt32.MaxValue
)
не меняется. И хотя это, конечно, не самое распространённое требование, например, оно может быть полезно в приложениях, обрабатывающих огромные изображения или большие данные.Продолжение следует…
Источник: https://dotnetmemoryexpert.com
День восемьсот тридцать первый. #Юмор
9 Квинтиллионов Остановок
Пост немножко боян, но всё-таки, довольно смешно. Итак, в твиттере выложили скриншот приложения Journey Tracker, используемого в Англии, в котором показан путь поезда с 9,223,372,036,854,775,807 остановок. Как такое возможно?
Программисты, наверное, сразу попробуют сравнить это число со степенью двойки, и действительно, оно всего на 1 меньше 2^63, что очень похоже на 64-битное число со знаком. То есть имело место простое переполнение.
На самом деле на приведённой картинке был первый случай. Через пару лет случился похожий (который подробно описан в видео по ссылке ниже). Отдельно доставило, что на второй твит ответил один из разработчиков приложения: «Я думал, мы это пофиксили давным давно.»
Гляньте видео, кому интересно, там подробности и немного бинарной математики. А вообще, советую очень интересный канал Stand-up Maths про прикладную математику в самых разных сферах жизни.
Источник: https://youtu.be/48QQXpbTlVM
9 Квинтиллионов Остановок
Пост немножко боян, но всё-таки, довольно смешно. Итак, в твиттере выложили скриншот приложения Journey Tracker, используемого в Англии, в котором показан путь поезда с 9,223,372,036,854,775,807 остановок. Как такое возможно?
Программисты, наверное, сразу попробуют сравнить это число со степенью двойки, и действительно, оно всего на 1 меньше 2^63, что очень похоже на 64-битное число со знаком. То есть имело место простое переполнение.
На самом деле на приведённой картинке был первый случай. Через пару лет случился похожий (который подробно описан в видео по ссылке ниже). Отдельно доставило, что на второй твит ответил один из разработчиков приложения: «Я думал, мы это пофиксили давным давно.»
Гляньте видео, кому интересно, там подробности и немного бинарной математики. А вообще, советую очень интересный канал Stand-up Maths про прикладную математику в самых разных сферах жизни.
Источник: https://youtu.be/48QQXpbTlVM
День восемьсот тридцать второй. #ЗаметкиНаПолях
Добавляем MVC в приложении ASP.NET Core
Сегодня обсудим методы
Для начала подробно рассмотрим, какие функции доступны в каждом методе.
Все методы поддерживают:
- Контроллеры
- Связывание модели
- Авторизацию
- Валидацию
- API Explorer
- CORS
- Форматирование вывода
- Antiforgery (защита от атак CSRF)
- TempData
- Представления
- Страницы (Razor Pages)
- Тег-хелперы
- Кеш в памяти
Основные моменты
- Если вам нужны только контроллеры, включая привязку модели (выбор подходящего метода действия контроллера), авторизацию и валидацию данных, вы можете использовать любой из методов.
- ApiExplorer содержит функции для обнаружения и предоставления метаданных о вашем приложении MVC. Он используется для предоставления таких сведений, как список контроллеров и действий, их URL-адреса и разрешенные методы HTTP, параметры и типы ответов. Например, его использует Swagger для генерации документации API. ApiExplorer доступен во всех методах, кроме
- CORS позволяет серверу ослабить политику одного источника (same-origin policy). Система безопасности браузера запрещает клиентскому коду страницы делать запросы к домену, отличному от того, который обслуживает веб-страницу. Это ограничение называется политика одного источника. Она предотвращает чтение вредоносным сайтом конфиденциальных данных с другого сайта. Иногда может потребоваться разрешить другим сайтам выполнять запросы между разными доменами в приложении. За это отвечает CORS, который доступен везде, кроме
- Форматирование вывода (Formatter Mapping) используется для возможности выводить ответ в различных форматах, таких как JSON, XML и т.п. Опять же, доступно везде, кроме
- Функция кэширования в памяти через
Какой метод использовать для вашего приложения?
Это зависит от того, какое приложение вы хотите создать.
- Если вы хотите создать приложение веб-API без представлений, используйте
- Если вы хотите работать с приложением на Razor Pages, используйте
- Если вы хотите использовать MVC, используйте
Таким образом,
Источник: https://dotnettutorials.net/lesson/difference-between-addmvc-and-addmvccore-method/
Добавляем MVC в приложении ASP.NET Core
Сегодня обсудим методы
AddController()
, AddMvc()
, AddControllersWithViews()
и AddRazorPages()
в приложениях ASP.NET Core. Всё это методы расширения IServiceCollection
. Они используются в методе ConfigureServices()
класса Startup
. На первый взгляд, всё вроде очевидно, но что же конкретно делает каждый из них, и когда какой использовать?Для начала подробно рассмотрим, какие функции доступны в каждом методе.
Все методы поддерживают:
- Контроллеры
- Связывание модели
- Авторизацию
- Валидацию
AddController()
помимо общих функций поддерживает:- API Explorer
- CORS
- Форматирование вывода
AddRazorPages()
помимо общих функций поддерживает:- Antiforgery (защита от атак CSRF)
- TempData
- Представления
- Страницы (Razor Pages)
- Тег-хелперы
- Кеш в памяти
AddControllersWithViews()
поддерживает всё, что есть в AddController()
и AddRazorPages()
, кроме Страниц (т.е. только чистый MVC).AddMvc()
поддерживает всё, включая Страницы.Основные моменты
- Если вам нужны только контроллеры, включая привязку модели (выбор подходящего метода действия контроллера), авторизацию и валидацию данных, вы можете использовать любой из методов.
- ApiExplorer содержит функции для обнаружения и предоставления метаданных о вашем приложении MVC. Он используется для предоставления таких сведений, как список контроллеров и действий, их URL-адреса и разрешенные методы HTTP, параметры и типы ответов. Например, его использует Swagger для генерации документации API. ApiExplorer доступен во всех методах, кроме
AddRazorPages()
.- CORS позволяет серверу ослабить политику одного источника (same-origin policy). Система безопасности браузера запрещает клиентскому коду страницы делать запросы к домену, отличному от того, который обслуживает веб-страницу. Это ограничение называется политика одного источника. Она предотвращает чтение вредоносным сайтом конфиденциальных данных с другого сайта. Иногда может потребоваться разрешить другим сайтам выполнять запросы между разными доменами в приложении. За это отвечает CORS, который доступен везде, кроме
AddRazorPages()
.- Форматирование вывода (Formatter Mapping) используется для возможности выводить ответ в различных форматах, таких как JSON, XML и т.п. Опять же, доступно везде, кроме
AddRazorPages()
.- Функция кэширования в памяти через
IMemoryCache
недоступна только в методе AddControllers()
, но доступна в остальных трёх.Какой метод использовать для вашего приложения?
Это зависит от того, какое приложение вы хотите создать.
- Если вы хотите создать приложение веб-API без представлений, используйте
AddControllers()
.- Если вы хотите работать с приложением на Razor Pages, используйте
AddRazorPages()
.- Если вы хотите использовать MVC, используйте
AddControllersWithViews()
. А если в этом приложении вам требуются и Razor Pages, используйте AddMVC()
.Таким образом,
AddMvc()
включает все возможности. То есть вы можете использовать любой тип приложения (веб-API, MVC и Razor Pages). Однако, имейте в виду, что использование метода AddMvc()
добавляет все функции, даже если они вам не требуются, что может повлиять на производительность приложения.Источник: https://dotnettutorials.net/lesson/difference-between-addmvc-and-addmvccore-method/
День восемьсот тридцать третий. #ЧтоНовенького
.NET 6: Минималистические API
В Microsoft всерьёз озаботились слишком высоким порогом входа в ASP.NET, по сравнению с другими фреймворками. Проблема в том, что минимальный проект API или веб-сайта в ASP.NET содержит с десяток файлов и папок: файлы конфигурации,
В итоге в Microsoft пришли к следующему коду в файле
И всё! Вот полноценное рабочее веб-API приложение (можно дальше добавлять
Никаких
На мой взгляд, это великолепное нововведение! Как объясняют создатели (ссылка на полное видео внизу), это позволит новичкам не погружаться сразу в бессмысленный набор файлов, а начать с самого простого. Один файл. Задаёшь путь и нужный вывод (см. "Hello World" выше), переходишь по этому пути, и получаешь результат!
При этом самое главное, что это не обязаловка. Нынешний хорошо структурированный расклад не только продолжит существовать, но также будут добавлены все возможные вариации между ним и этим однофайловым кодом. То есть, вместо
Ну и конечно, такой подход сильно поможет в создании микросервисов, когда нужен самый простой код, «без ансамбля». Помимо прочего, судя по первым тестам, он работает на 30-50% быстрее стандартного приложения из-за избавления от всего ненужного функционала.
Источник: https://youtu.be/enAskgcF0c0
.NET 6: Минималистические API
В Microsoft всерьёз озаботились слишком высоким порогом входа в ASP.NET, по сравнению с другими фреймворками. Проблема в том, что минимальный проект API или веб-сайта в ASP.NET содержит с десяток файлов и папок: файлы конфигурации,
Startup.cs
и т.п. А кто знает точно, что вообще происходит в файлах Program.cs
и Startup.cs
? Зачем там весь этот код?В итоге в Microsoft пришли к следующему коду в файле
Program.cs
:var app = WebApplication.Create(args);Помимо этого, конечно, нужны классы домена и контекста
app.Map("/", () => "Hello World");
app.MapGet("/todos", async () =>
{
using var db = new TodoDbContext();
return await db.Todos.ToListAsync();
});
app.MapPost("/todos", async (Todo todo) =>
{
using var db = new TodoDbContext();
await db.Todos.AddAsync(todo);
await db.SaveChangesAsync();
});
app.Run();
TodoDbContext
. И понадобится ещё файл Imports.cs
, где будут находиться все директивы using, используемые в приложении (их теперь, чтоб не писать в каждом файле, можно подключить глобально через global using …
, по аналогии с файлом ViewImports
).И всё! Вот полноценное рабочее веб-API приложение (можно дальше добавлять
MapDelete
, MapPatch
и т.п.). Как вам?Никаких
CreateHostBuilder
, UseStartup
, Configure
и ConfigureServices
, папки контроллеров и прочего шаблонного кода. Все настройки по умолчанию собраны в WebApplication.Create
, а дальше вы просто сопоставляете путь с функцией обработчиком, и вуаля! На мой взгляд, это великолепное нововведение! Как объясняют создатели (ссылка на полное видео внизу), это позволит новичкам не погружаться сразу в бессмысленный набор файлов, а начать с самого простого. Один файл. Задаёшь путь и нужный вывод (см. "Hello World" выше), переходишь по этому пути, и получаешь результат!
При этом самое главное, что это не обязаловка. Нынешний хорошо структурированный расклад не только продолжит существовать, но также будут добавлены все возможные вариации между ним и этим однофайловым кодом. То есть, вместо
WebApplication.Create
вы можете создать построитель, и внедрить нужные зависимости:var builder = WebApplication.CreateBuilder(args);Далее можно более детально настроить хост, добавить необходимые сервисы. Затем можно выделить настройку сервисов в файл Startup, а сопоставление путей с методами в отдельный файл, а дальше разделить на контроллеры. То есть обучение технологии теперь будет проходить более естественно. Не начиная с многостраничного объяснения, что, где и зачем лежит. Теперь можно начать с элементарного рабочего кода, а дальше (при необходимости) выделять обособленные части в отдельные файлы и постепенно перейти к той структуре, которая нам всем известна.
builder.Services
.AddDbContext<TodoDbContext>(o =>
o.UseSqlite("DataSource=Todos.db"));
var app = builder.Build();
Ну и конечно, такой подход сильно поможет в создании микросервисов, когда нужен самый простой код, «без ансамбля». Помимо прочего, судя по первым тестам, он работает на 30-50% быстрее стандартного приложения из-за избавления от всего ненужного функционала.
Источник: https://youtu.be/enAskgcF0c0
День восемьсот тридцать четвёртый. #ЧтоНовенького
Обновление Ветки из Upstream в GitHub
В UI GitHub появилась кнопка, которая позволяет вам обновить вашу fork-ветку из основной upstream-ветки в два клика, вместо того, чтобы писать команды в консоли.
Над списком файлов в вашем репозитории справа появилось меню Fetch upstream, при нажатии на которое всплывает окно, показанное на рисунке. Вы можете сравнить ветки или получить изменения и слить их. Если не возникнет конфликтов, ваша ветка обновится из upstream.
Учитывая, что чтобы сделать это через CLI, часто приходилось гуглить нужные команды, это определённо полезное нововведение на сайте GitHub.
Источник: https://ardalis.com/github-fetch-upstream/
Обновление Ветки из Upstream в GitHub
В UI GitHub появилась кнопка, которая позволяет вам обновить вашу fork-ветку из основной upstream-ветки в два клика, вместо того, чтобы писать команды в консоли.
Над списком файлов в вашем репозитории справа появилось меню Fetch upstream, при нажатии на которое всплывает окно, показанное на рисунке. Вы можете сравнить ветки или получить изменения и слить их. Если не возникнет конфликтов, ваша ветка обновится из upstream.
Учитывая, что чтобы сделать это через CLI, часто приходилось гуглить нужные команды, это определённо полезное нововведение на сайте GitHub.
Источник: https://ardalis.com/github-fetch-upstream/
День восемьсот тридцать пятый. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 17-20
Начало 1-4
Продолжение 5-8
Продолжение 9-12
Продолжение 13-16
17. Зачем классу финализатор?
Финализатор - это специальный метод, который вызывается средой выполнения после того, как объект будет помечен как недостижимый. Другими словами, это специальный метод, вызываемый перед сборкой мусора. Таким образом, он рассматривается как подстраховка для высвобождения некоторых неуправляемых ресурсов для объектов, содержащих их. Обычно это какой-нибудь «дескриптор» файла, сокета или что-то подобное. Предпочтительным способом является использование контракта
Заметьте, что
- точное время, когда именно будет вызван финализатор, не определено.
- порядок вызова финализаторов в объектах (даже связанных друг с другом) не гарантируется.
18. Можно ли узнать о сборке мусора или управлять ей?
К сожалению, в настоящее время нет доступного API, который будет информировать о происходящей в данный момент сборке мусора. Более того, нет даже надёжного API, который предупредит нас о грядущей сборке. Существует метод
С другой стороны, есть метод
19. Зачем используется управляемый указатель (ref)?
Управляемый указатель (также известный как
Управляемый указатель довольно часто используется в низкоуровневом производительном коде для непосредственного управления данными, особенно в сочетании со структурами и/или объектами
20. Чем отличается серверная сборка мусора?
Серверная сборка мусора - это режим, предназначенный для приложений с одновременной обработкой запросов, как правило, веб-приложений. Но её с успехом можно использовать и в любом другом типе приложений с аналогичными характеристиками (например, сервисом, обрабатывающим сообщения из очереди). То есть в приложениях, где нас больше всего волнует пропускная способность - обработка как можно большего количества запросов в секунду. Таким образом, в отличие от сборки мусора в режиме рабочей станции, серверная не так сильно озабочена паузами (задержкой), потому что нет прямого пользователя (например, взаимодействующего с пользовательским интерфейсом), на которого они повлияют.
И поскольку мы предполагаем, что приложение «серверное», оно также более охотно потребляет память и потоки ЦП для достижения своих целей.
Продолжение следует…
Источник: https://dotnetmemoryexpert.com
Топ Вопросов о Памяти в .NET. Продолжение 17-20
Начало 1-4
Продолжение 5-8
Продолжение 9-12
Продолжение 13-16
17. Зачем классу финализатор?
Финализатор - это специальный метод, который вызывается средой выполнения после того, как объект будет помечен как недостижимый. Другими словами, это специальный метод, вызываемый перед сборкой мусора. Таким образом, он рассматривается как подстраховка для высвобождения некоторых неуправляемых ресурсов для объектов, содержащих их. Обычно это какой-нибудь «дескриптор» файла, сокета или что-то подобное. Предпочтительным способом является использование контракта
IDisposable
и освобождение этих ресурсов путем явного вызова Dispose
(или с помощью директивы using
). Но если программист забудет это сделать, финализатор придёт на помощь. Таким образом, классу может потребоваться финализатор, чтобы убедиться, что неуправляемый ресурс, которым он владеет, будет очищен. Финализатор, хотя обычно используется вместе с IDisposable
, никак не связан с ним. Заметьте, что
- точное время, когда именно будет вызван финализатор, не определено.
- порядок вызова финализаторов в объектах (даже связанных друг с другом) не гарантируется.
18. Можно ли узнать о сборке мусора или управлять ей?
К сожалению, в настоящее время нет доступного API, который будет информировать о происходящей в данный момент сборке мусора. Более того, нет даже надёжного API, который предупредит нас о грядущей сборке. Существует метод
GC.RegisterForFullGCNotification
, который (как сказано в документации) выдаёт уведомление, «когда среда выполнения чувствует, что приближается полная сборка мусора». Но это «чувство» не является точным и также зависит от магического порога, который придётся тонко настроить в соответствии с рабочей нагрузкой вашего приложения методом проб и ошибок. Этот метод не рекомендуется использовать.С другой стороны, есть метод
GC.Collect
для запуска сборки мусора и менее известный метод GC.TryStartNoGCRegion
, чтобы попытаться отключить сборку мусора на заданное количество аллокаций памяти.19. Зачем используется управляемый указатель (ref)?
Управляемый указатель (также известный как
ref
или byref
) - это особый тип указателя на разные места в памяти: данные стека, внутренние объекты или массивы или неуправляемые данные. И поскольку он может указывать на данные стека, есть серьёзные ограничения на то, где мы можем его использовать. Например, в настоящее время запрещено использовать его в качестве поля класса, потому что он может прожить дольше, чем адрес в стеке, на который он указывает. Запрещено даже использовать его в качестве поля структуры (всегда живущей в стеке), хотя это ограничение может быть ослаблено в будущем.Управляемый указатель довольно часто используется в низкоуровневом производительном коде для непосредственного управления данными, особенно в сочетании со структурами и/или объектами
Span<T>
(которые, кстати, под капотом также используют управляемые указатели).20. Чем отличается серверная сборка мусора?
Серверная сборка мусора - это режим, предназначенный для приложений с одновременной обработкой запросов, как правило, веб-приложений. Но её с успехом можно использовать и в любом другом типе приложений с аналогичными характеристиками (например, сервисом, обрабатывающим сообщения из очереди). То есть в приложениях, где нас больше всего волнует пропускная способность - обработка как можно большего количества запросов в секунду. Таким образом, в отличие от сборки мусора в режиме рабочей станции, серверная не так сильно озабочена паузами (задержкой), потому что нет прямого пользователя (например, взаимодействующего с пользовательским интерфейсом), на которого они повлияют.
И поскольку мы предполагаем, что приложение «серверное», оно также более охотно потребляет память и потоки ЦП для достижения своих целей.
Продолжение следует…
Источник: https://dotnetmemoryexpert.com
День восемьсот тридцать шестой. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
85. Простые системы и закон Галла
Когда вам нужно будет создать новую большую систему, чтобы заменить старую, помните о законе Галла и о пользе эволюционного развития и частой обратной связи.
Закон Галла гласит:
Всегда обнаруживается, что сложная работающая система произошла от простой работающей системы. Сложная система, созданная с нуля, никогда не работает и невозможно заставить её работать, как надо. Вы должны начать с работающей простой системы.
Прежде чем двигаться дальше, важно обратить внимание на первое предложение. Работающая сложная система, и простая система тоже работающая! В любом другом случае мы можем игнорировать этот закон.
Совершенно очевидно, что небольшие и более простые системы легче построить и заставить работать. Во-первых, легче определить, что небольшая простая система действительно работает. Систему с минимальным охватом довольно легко проверить даже с помощью элементарного ручного тестирования.
Более крупные и сложные системы часто имеют поведение, которое трудно диагностировать, причём в одном система может вести себя, как задумано, а в другом нет. Многие успешные системы, которые мы используем каждый день, построены из более мелких компонентов, каждый из которых может быть протестирован независимо.
Большое начинается с малого
Часто это не очевидно, но любое большое событие всегда можно проследить до маленького факта. Обычно их много: множество небольших идей, решений, удачных совпадений, которые привели к большим изменениям. Мы можем извлечь уроки из этого и иметь это в виду, когда проектируем и пытаемся создать амбициозные программные системы. Прежде чем пытаться создать работающую сложную систему, сначала создайте небольшую работающую систему.
Экстремальное программирование
Одна из ключевых ценностей экстремального программирования (XP) - это простота в сочетании с практикой простого дизайна. По сути, практика XP вращается вокруг частой доставки работающего ПО и его изменения по мере необходимости для удовлетворения текущих потребностей клиентов или пользователей. Экстремальное программирование серьезно относится к закону Галла и встраивает его в процесс разработки.
Архитектура, Монолиты, Микросервисы
Мы можем попробовать применить закон Галла к архитектуре ПО, особенно для крупных и амбициозных проектов. Вместо того, чтобы пытаться написать подробную спецификацию большой системы, которую мы хотим переписать, часто лучше определить блоки существующей системы, которые хорошо работают, и переносить их в новую систему по частям, сохраняя при этом их работоспособность. Или, если это нецелесообразно, заменить существующую функциональность новой функциональностью, которая обёртывает старую систему, используя шаблоны вроде Strangler, чтобы в итоге можно было избавиться от частей старой системы.
Что проще и быстрее построить: монолит или набор микросервисов? Монолит, всегда. Так что сначала подумайте о создании модульного монолита и рассмотрите микросервисы, когда у вас будет простая рабочая система.
Одним из ключевых моментов микросервисов является то, что они представляют собой простые рабочие системы. Поэтому убедитесь, что вы не упустите это из виду, если и когда решите их строить. Делайте их простыми, работающими и, в идеале, развёртывайте их, когда они стабильны и надёжны. Пока они не станут надёжными и относительно стабильными (по крайней мере, с точки зрения их общедоступного интерфейса), подумайте о том, чтобы сохранить их в качестве модулей или библиотек в системе, которая нуждается в их функциональности. Сохраняйте вещи простыми и работающими как можно дольше, а когда вам потребуется архитектурная сложность микросервисов, убедитесь, что вы применяете её к частям системы, которые самостоятельны, просты и работают.
Источник: https://ardalis.com/simple-systems-galls-law/
97 Вещей, Которые Должен Знать Каждый Программист
85. Простые системы и закон Галла
Когда вам нужно будет создать новую большую систему, чтобы заменить старую, помните о законе Галла и о пользе эволюционного развития и частой обратной связи.
Закон Галла гласит:
Всегда обнаруживается, что сложная работающая система произошла от простой работающей системы. Сложная система, созданная с нуля, никогда не работает и невозможно заставить её работать, как надо. Вы должны начать с работающей простой системы.
Прежде чем двигаться дальше, важно обратить внимание на первое предложение. Работающая сложная система, и простая система тоже работающая! В любом другом случае мы можем игнорировать этот закон.
Совершенно очевидно, что небольшие и более простые системы легче построить и заставить работать. Во-первых, легче определить, что небольшая простая система действительно работает. Систему с минимальным охватом довольно легко проверить даже с помощью элементарного ручного тестирования.
Более крупные и сложные системы часто имеют поведение, которое трудно диагностировать, причём в одном система может вести себя, как задумано, а в другом нет. Многие успешные системы, которые мы используем каждый день, построены из более мелких компонентов, каждый из которых может быть протестирован независимо.
Большое начинается с малого
Часто это не очевидно, но любое большое событие всегда можно проследить до маленького факта. Обычно их много: множество небольших идей, решений, удачных совпадений, которые привели к большим изменениям. Мы можем извлечь уроки из этого и иметь это в виду, когда проектируем и пытаемся создать амбициозные программные системы. Прежде чем пытаться создать работающую сложную систему, сначала создайте небольшую работающую систему.
Экстремальное программирование
Одна из ключевых ценностей экстремального программирования (XP) - это простота в сочетании с практикой простого дизайна. По сути, практика XP вращается вокруг частой доставки работающего ПО и его изменения по мере необходимости для удовлетворения текущих потребностей клиентов или пользователей. Экстремальное программирование серьезно относится к закону Галла и встраивает его в процесс разработки.
Архитектура, Монолиты, Микросервисы
Мы можем попробовать применить закон Галла к архитектуре ПО, особенно для крупных и амбициозных проектов. Вместо того, чтобы пытаться написать подробную спецификацию большой системы, которую мы хотим переписать, часто лучше определить блоки существующей системы, которые хорошо работают, и переносить их в новую систему по частям, сохраняя при этом их работоспособность. Или, если это нецелесообразно, заменить существующую функциональность новой функциональностью, которая обёртывает старую систему, используя шаблоны вроде Strangler, чтобы в итоге можно было избавиться от частей старой системы.
Что проще и быстрее построить: монолит или набор микросервисов? Монолит, всегда. Так что сначала подумайте о создании модульного монолита и рассмотрите микросервисы, когда у вас будет простая рабочая система.
Одним из ключевых моментов микросервисов является то, что они представляют собой простые рабочие системы. Поэтому убедитесь, что вы не упустите это из виду, если и когда решите их строить. Делайте их простыми, работающими и, в идеале, развёртывайте их, когда они стабильны и надёжны. Пока они не станут надёжными и относительно стабильными (по крайней мере, с точки зрения их общедоступного интерфейса), подумайте о том, чтобы сохранить их в качестве модулей или библиотек в системе, которая нуждается в их функциональности. Сохраняйте вещи простыми и работающими как можно дольше, а когда вам потребуется архитектурная сложность микросервисов, убедитесь, что вы применяете её к частям системы, которые самостоятельны, просты и работают.
Источник: https://ardalis.com/simple-systems-galls-law/
День восемьсот тридцать седьмой. #ЗаметкиНаПолях
Статические Методы - Зло? Начало
Использовать статические методы - хорошо или плохо? Попытаюсь описать эволюцию моего отношения к ним.
1. Ух ты, статические методы!
Впервые узнав о статических методах, большинство людей приходят в восторг. Это понятно, потому что у них есть довольно веские преимущества:
- Они удобны - вы можете вызывать их в любое время, не заботясь ни о каких надоедливых зависимостях.
- Они быстрее - статические методы немного быстрее, чем экземплярные, потому что в экземплярных вы также работаете с неявным параметром this. Исключение этого параметра дает небольшой прирост производительности в большинстве языков программирования.
Учитывая эти 2 преимущества, становится понятно, почему люди поначалу тяготеют к использованию статических методов.
2. Внедрение зависимости.
Через некоторое время вы узнаете о принципах ООП, SOLID и внедрении зависимостей. Вы начинаете понимать, что статические методы вносят некоторые высокоуровневые недостатки в дизайн вашего кода:
- Сильная связанность - код, вызывающий статические методы, тесно связан с вызываемым кодом. Отсутствие промежуточной абстракции затрудняет тестирование этого кода.
- Запутанный график зависимостей - код, заваленный вызовами статических методов, трудно понять, потому что поток зависимостей неявный, и вам нужно прочитать весь класс, чтобы увидеть полную картину.
Явные зависимости (вводимые как параметры конструктора или параметры метода) устраняют эти проблемы. Они позволяют вам вводить абстракции между вызывающим и вызываемым абонентами и, следовательно, использовать модульное тестирование. Они также делают график зависимостей понятным.
Явные зависимости также помогают сузить область действия этих зависимостей, делая их доступными для определенных классов, а не для всей базы кода. А чем уже сфера использования чего-либо, тем легче это понять.
3. Знакомство с функциональным программированием.
Последнее прозрение приходит, когда вы узнаете о функциональном программировании. Основная идея функционального программирования - избегать скрытых входных и выходных данных, таких как:
- Ссылка на изменяемое состояние,
- Модификация общего состояния (также известная как побочные эффекты),
- Использование исключений для управления ходом программы.
В функциональном программировании все функции должны иметь явные входные и выходные данные. Эта практика приводит к созданию кода, который чрезвычайно легко понять и поддерживать. Вы спросите, как все это относится к статическим методам?
Окончание следует…
Источник: https://enterprisecraftsmanship.com/posts/static-methods-evil/
Статические Методы - Зло? Начало
Использовать статические методы - хорошо или плохо? Попытаюсь описать эволюцию моего отношения к ним.
1. Ух ты, статические методы!
Впервые узнав о статических методах, большинство людей приходят в восторг. Это понятно, потому что у них есть довольно веские преимущества:
- Они удобны - вы можете вызывать их в любое время, не заботясь ни о каких надоедливых зависимостях.
- Они быстрее - статические методы немного быстрее, чем экземплярные, потому что в экземплярных вы также работаете с неявным параметром this. Исключение этого параметра дает небольшой прирост производительности в большинстве языков программирования.
Учитывая эти 2 преимущества, становится понятно, почему люди поначалу тяготеют к использованию статических методов.
2. Внедрение зависимости.
Через некоторое время вы узнаете о принципах ООП, SOLID и внедрении зависимостей. Вы начинаете понимать, что статические методы вносят некоторые высокоуровневые недостатки в дизайн вашего кода:
- Сильная связанность - код, вызывающий статические методы, тесно связан с вызываемым кодом. Отсутствие промежуточной абстракции затрудняет тестирование этого кода.
- Запутанный график зависимостей - код, заваленный вызовами статических методов, трудно понять, потому что поток зависимостей неявный, и вам нужно прочитать весь класс, чтобы увидеть полную картину.
Явные зависимости (вводимые как параметры конструктора или параметры метода) устраняют эти проблемы. Они позволяют вам вводить абстракции между вызывающим и вызываемым абонентами и, следовательно, использовать модульное тестирование. Они также делают график зависимостей понятным.
Явные зависимости также помогают сузить область действия этих зависимостей, делая их доступными для определенных классов, а не для всей базы кода. А чем уже сфера использования чего-либо, тем легче это понять.
3. Знакомство с функциональным программированием.
Последнее прозрение приходит, когда вы узнаете о функциональном программировании. Основная идея функционального программирования - избегать скрытых входных и выходных данных, таких как:
- Ссылка на изменяемое состояние,
- Модификация общего состояния (также известная как побочные эффекты),
- Использование исключений для управления ходом программы.
В функциональном программировании все функции должны иметь явные входные и выходные данные. Эта практика приводит к созданию кода, который чрезвычайно легко понять и поддерживать. Вы спросите, как все это относится к статическим методам?
Окончание следует…
Источник: https://enterprisecraftsmanship.com/posts/static-methods-evil/
День восемьсот тридцать восьмой. #ЗаметкиНаПолях
Статические Методы - Зло? Окончание
Начало
Когда вы изучаете функциональное программирование, вы начинаете понимать, что статические методы вредны только в том случае, если они работают с общим состоянием. Например, когда они позволяют вам читать или изменять состояние глобальной переменной.
Статические методы, которые не работают с общим состоянием, по-прежнему обладают хорошими характеристиками, которые мы обсуждали ранее (удобство, немного лучшая производительность), но не проявляют соответствующих недостатков (сильная связанность, запутанный график зависимостей).
Единственная причина, по которой эти недостатки становятся проблемой, заключается в том, что вам нужно отслеживать изменения состояния на протяжении всего потока выполнения вашей программы. Без изменения состояния эти два момента перестают быть проблемой.
Вы же не беспокоитесь об "абстрагировании" вызовов вроде
Рассмотрим следующий класс:
4. Вывод
За свою карьеру программиста я испытывал разное отношение к использованию статических методов. Ещё недавно я пытался всегда явно вводить все нужные зависимости. В настоящее время я делаю это только для зависимостей, которые сохраняют состояние. Если метод не сохраняет состояния, его можно сделать статическим и вызвать напрямую.
Тестировать статические методы также проще, чем экземплярные, потому что они не требуют создания экземпляра класса.
Источник: https://enterprisecraftsmanship.com/posts/static-methods-evil/
Статические Методы - Зло? Окончание
Начало
Когда вы изучаете функциональное программирование, вы начинаете понимать, что статические методы вредны только в том случае, если они работают с общим состоянием. Например, когда они позволяют вам читать или изменять состояние глобальной переменной.
Статические методы, которые не работают с общим состоянием, по-прежнему обладают хорошими характеристиками, которые мы обсуждали ранее (удобство, немного лучшая производительность), но не проявляют соответствующих недостатков (сильная связанность, запутанный график зависимостей).
Единственная причина, по которой эти недостатки становятся проблемой, заключается в том, что вам нужно отслеживать изменения состояния на протяжении всего потока выполнения вашей программы. Без изменения состояния эти два момента перестают быть проблемой.
Вы же не беспокоитесь об "абстрагировании" вызовов вроде
Math.Min(a, b)
, верно? Вам также не следует беспокоиться об отделении вашего кода от ваших собственных статических методов, которые не сохраняют состояния.Рассмотрим следующий класс:
public class CustomerService {Обычно мы сделали бы методы
public void Process(string cust, string addr)
{
Address a = CreateAddress(addr);
Customer c = CreateCustomer(cust, a);
SaveCustomer(c);
}
…
}
CreateAddress
, CreateCustomer
и SaveCustomer
приватными экземплярными методами. Но, их можно сделать статическими: private static Address CreateAddress(string addr)Преимущество этой версии в том, что статические методы заставляют вас следовать принципам функционального программирования. Становится невозможным по ошибке сослаться на состояние класса внутри метода, и это заставляет вас явно указывать в методах, какие данные им нужны, и какие они производят на выходе.
=> new Address(addr);
private static Customer
CreateCustomer(string name, Address addr)
=> new Customer(name, addr);
private static void SaveCustomer(Customer c)
{
var repository = new Repository();
repository.Save(c);
}
4. Вывод
За свою карьеру программиста я испытывал разное отношение к использованию статических методов. Ещё недавно я пытался всегда явно вводить все нужные зависимости. В настоящее время я делаю это только для зависимостей, которые сохраняют состояние. Если метод не сохраняет состояния, его можно сделать статическим и вызвать напрямую.
Тестировать статические методы также проще, чем экземплярные, потому что они не требуют создания экземпляра класса.
Источник: https://enterprisecraftsmanship.com/posts/static-methods-evil/
День восемьсот тридцать девятый. #ЗаметкиНаПолях #GC
Топ Вопросов о Памяти в .NET. Продолжение 21-24
Начало 1-4
Продолжение 5-8
Продолжение 9-12
Продолжение 13-16
Продолжение 17-20
21. Как измерить время паузы на сборку мусора?
Каждая пауза на сборку мусора может вызвать нежелательные задержки - будь то паузы, которые заметит пользователь, или более длительная обработка HTTP-запроса. К сожалению, на момент .NET 5 нет поддержки в их измерении. Нет специального счетчика производительности или счетчика событий. И подходящего API сборщика мусора также нет. Но это не значит, что мы не можем это измерить! Лучший способ - собрать трассировку с помощью инструмента PerfView или
22. В чём особенность типа ref struct?
Ref struct - это особый тип структуры, представленный в C# 7.2. Он накладывает множество ограничений на поведение структуры с целью избежать попадания экземпляра ref struct в управляемую кучу. Обычная структура может попасть туда, например, при явной или неявной упаковке или если она является полем класса.
Все эти условия запрещены для
Но наиболее важным результатом этих ограничений является то, что она может содержать управляемый указатель. Или типы, содержащие управляемый указатель (например,
23. Циклические ссылки замедляют на сборку мусора и влияют на производительность?
Циклические ссылки обычно проблематичны для алгоритмов сборки мусора, использующих подсчёт ссылок. В наивных реализациях объекты, образующие циклическую ссылку, не могут быть собраны, потому что всегда есть по крайней мере один объект, указывающий на другой. Есть более сложные алгоритмы подсчёта ссылок с некоторыми дополнительными накладными расходами для обработки циклических ссылок.
Но .NET GC не использует подсчёт ссылок. Он использует так называемую трассировочную сборку мусора, которая основана на «отслеживании» того, что действительно всё ещё доступно из кода. Такая трассировка является умной - она посещает каждый объект только один раз, даже если между объектами существуют сложные зависимости. Другими словами, циклические ссылки вообще не влияют на производительность сборки мусора в .NET.
24. Чем ValueTask «лучше» Task в смысле потребляемой памяти?
Продолжение следует…
Источник: https://dotnetmemoryexpert.com
Топ Вопросов о Памяти в .NET. Продолжение 21-24
Начало 1-4
Продолжение 5-8
Продолжение 9-12
Продолжение 13-16
Продолжение 17-20
21. Как измерить время паузы на сборку мусора?
Каждая пауза на сборку мусора может вызвать нежелательные задержки - будь то паузы, которые заметит пользователь, или более длительная обработка HTTP-запроса. К сожалению, на момент .NET 5 нет поддержки в их измерении. Нет специального счетчика производительности или счетчика событий. И подходящего API сборщика мусора также нет. Но это не значит, что мы не можем это измерить! Лучший способ - собрать трассировку с помощью инструмента PerfView или
dotnet trace
и просмотреть отчет GC Stats в PerfView.22. В чём особенность типа ref struct?
Ref struct - это особый тип структуры, представленный в C# 7.2. Он накладывает множество ограничений на поведение структуры с целью избежать попадания экземпляра ref struct в управляемую кучу. Обычная структура может попасть туда, например, при явной или неявной упаковке или если она является полем класса.
Все эти условия запрещены для
ref struct
. Благодаря этому гарантировано, что она всегда будет находиться в стеке (или регистрах процессора). Это ограничение может быть интересным само по себе: структура данных в стеке не требует многопоточной синхронизации, поскольку по умолчанию к ней нельзя получить доступ из нескольких потоков. И у нас есть гарантия, что она никогда не повлияет на сборщик мусора.Но наиболее важным результатом этих ограничений является то, что она может содержать управляемый указатель. Или типы, содержащие управляемый указатель (например,
Span<T>
). Таким образом, это довольно популярный тип, используемый в низкоуровневом программировании.23. Циклические ссылки замедляют на сборку мусора и влияют на производительность?
Циклические ссылки обычно проблематичны для алгоритмов сборки мусора, использующих подсчёт ссылок. В наивных реализациях объекты, образующие циклическую ссылку, не могут быть собраны, потому что всегда есть по крайней мере один объект, указывающий на другой. Есть более сложные алгоритмы подсчёта ссылок с некоторыми дополнительными накладными расходами для обработки циклических ссылок.
Но .NET GC не использует подсчёт ссылок. Он использует так называемую трассировочную сборку мусора, которая основана на «отслеживании» того, что действительно всё ещё доступно из кода. Такая трассировка является умной - она посещает каждый объект только один раз, даже если между объектами существуют сложные зависимости. Другими словами, циклические ссылки вообще не влияют на производительность сборки мусора в .NET.
24. Чем ValueTask «лучше» Task в смысле потребляемой памяти?
ValueTask
- это структура. Таким образом, используя его, мы получаем выгоду от того, что экземпляр не будет размещен в управляемой куче. Это может произойти в «счастливом» синхронном пути асинхронной операции - мы уже знаем результат, поэтому мы можем передать его через структуру, вместо того чтобы выделять Task только для передачи результата. Однако мы не можем гарантировать, что ValueTask
никогда не будет упакован, потому что в «несчастливом» асинхронном случае он может быть упакован внутри конечного автомата, отслеживающего операцию.Продолжение следует…
Источник: https://dotnetmemoryexpert.com
День восемьсот сороковой. #PerformanceTips
5 Способов Повысить Производительность Кода C# Бесплатно
Разработка программного обеспечения - это поиск компромиссов:
- нормализация против денормализации в реляционных базах данных,
- скорость разработки против качественного кода
и т.п.
Высокопроизводительный код C# обычно требует компромиссов. Разработчики могут пожертвовать удобством сопровождения или безопасностью кода, чтобы код работал быстрее. Но это применимо только к сценариям, в которых уже применяются все шаблоны производительности и лучшие практики, а производительность всё равно требует дальнейшего улучшения.
Существует множество подходов, которые могут помочь разработчикам значительно улучшить производительность приложений, ничем не жертвуя.
Начнём с очевидного:
1. Указывайте ёмкость коллекции
Рассмотрим два почти идентичных метода:
Предварительное указание ёмкости устраняет накладные расходы на выделение, копирование и сборку мусора использованных массивов. Разработчики должны всегда указывать ёмкость коллекции, если они заранее знают, сколько элементов будет в неё добавлено.
Параметр ёмкости работает не только с коллекцией
Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da
5 Способов Повысить Производительность Кода C# Бесплатно
Разработка программного обеспечения - это поиск компромиссов:
- нормализация против денормализации в реляционных базах данных,
- скорость разработки против качественного кода
и т.п.
Высокопроизводительный код C# обычно требует компромиссов. Разработчики могут пожертвовать удобством сопровождения или безопасностью кода, чтобы код работал быстрее. Но это применимо только к сценариям, в которых уже применяются все шаблоны производительности и лучшие практики, а производительность всё равно требует дальнейшего улучшения.
Существует множество подходов, которые могут помочь разработчикам значительно улучшить производительность приложений, ничем не жертвуя.
Начнём с очевидного:
1. Указывайте ёмкость коллекции
Рассмотрим два почти идентичных метода:
public void NonFixedCapacityTest()Оба метода выполняют одну и ту же задачу - заполнение коллекции целыми числами с помощью цикла
{
var items = new List<decimal>();
for (int i = 0; i < 1000000; i++)
items.Add(i);
}
public void FixedCapacityTest()
{
const int capacity = 1000000;
var items = new List<decimal>(capacity);
for (int i = 0; i < capacity; i++)
items.Add(i);
}
foreach
. Единственное отличие состоит в том, что в методе FixedCapacityTest
конструктор коллекции инициализируется некоторым числом. Этот простой трюк заставляет метод FixedCapacityTest
работать в два раза быстрее, чем NonFixedCapacityTest
.| Method | Mean |Производительность в два-три раза выше, потому что
|--------------------- |----------:|
| NonFixedCapacityTest | 22.708 ms |
| FixedCapacityTest | 8.418 ms |
List<T>
реализован таким образом, что хранит элементы в массиве, который представляет собой структуру данных фиксированного размера. Когда разработчик создает экземпляр List<T>
без указания его ёмкости, выделяется массив ёмкости по умолчанию. Когда массив заполнен, выделяется новый массив большего размера, а значения из старого массива копируются в новый.Предварительное указание ёмкости устраняет накладные расходы на выделение, копирование и сборку мусора использованных массивов. Разработчики должны всегда указывать ёмкость коллекции, если они заранее знают, сколько элементов будет в неё добавлено.
Параметр ёмкости работает не только с коллекцией
List
, но и с другими, такими как Dictionary<TKey, TValue>
, HashSet<T>
и т.п.Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da
День восемьсот сорок первый. #PerformanceTips
5 Способов Повысить Производительность Кода C# Бесплатно
2. Используйте структуры вместо классов в некоторых случаях
Разработчикам часто может потребоваться выделить массив или список для хранения десятков тысяч объектов в памяти. Эту задачу можно решить с помощью класса или структуры.
Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da
5 Способов Повысить Производительность Кода C# Бесплатно
2. Используйте структуры вместо классов в некоторых случаях
Разработчикам часто может потребоваться выделить массив или список для хранения десятков тысяч объектов в памяти. Эту задачу можно решить с помощью класса или структуры.
public class PointClassКак видите, единственная разница между
{
public int X { get; set; }
public int Y { get; set; }
}
public struct PointStruct
{
public int X { get; set; }
public int Y { get; set; }
}
public void ListOfObjectsTest()
{
const int length = 1000000;
var items = new List<PointClass>(length);
for (int i = 0; i < length; i++)
items.Add(new PointClass() { X = i, Y = i });
}
public void ListOfStructsTest()
{
const int length = 1000000;
var items = new List<PointStruct>(length);
for (int i = 0; i < length; i++)
items.Add(new PointStruct() { X = i, Y = i});
}
ListOfObjectTest
и ListOfStructsTest
заключается в том, что первый создаёт экземпляры класса, а второй - экземпляры структур. Код PointClass
идентичен коду PointStruct
.| Method | Mean |Код, использующий структуры, работает в 10-15 раз быстрее, чем код, использующий классы. Такая большая разница во времени, объясняется тем, что в случае классов CLR должна выделить один миллион объектов в управляемой куче и сохранить ссылки на них в коллекции
|------------------ |----------:|
| ListOfObjectsTest | 67.724 ms |
| ListOfStructsTest | 5.136 ms |
List<T>
. В случае структур единственным объектом, размещённым в куче, будет экземпляр коллекции List<T>
. Миллион структур будет встроен в этот единственный экземпляр коллекции.Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da
День восемьсот сорок второй. #PerformanceTips
5 Способов Повысить Производительность Кода C# Бесплатно
3. Распараллеливание циклов
Часто бывает необходимо перебрать коллекцию с помощью цикла
Производительность можно повысить, начав использовать параллельную версию цикла
- разобьёт коллекцию на части,
- назначит и выполнит эти части в отдельных потоках.
Функциональные возможности, предоставляемые классом Parallel, являются отличным выбором для длительных итераций, которые не зависят друг от друга и не требуют использования примитивов синхронизации, таких как потокобезопасные коллекции.
Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da
5 Способов Повысить Производительность Кода C# Бесплатно
3. Распараллеливание циклов
Часто бывает необходимо перебрать коллекцию с помощью цикла
foreach
и выполнить некоторую логику для каждого элемента.public void ForeachTest()В этом примере итерации выполняются одна за другой в одном и том же потоке, поэтому общее время выполнения будет линейно расти с размером коллекции.
{
var items = Enumerable.Range(0, 100).ToList();
foreach (var item in items)
{
//Симулируем длинную операцию
Thread.Sleep(1);
}
}
Производительность можно повысить, начав использовать параллельную версию цикла
foreach
, которую платформа предоставляет разработчикам.public void ParallelForeachTest()
{
var items = Enumerable.Range(0, 100).ToList();
Parallel.ForEach(items, (item) =>
{
//Симулируем длинную операцию
Thread.Sleep(1);
});
}
Parallel.Foreach
можно использовать на любой коллекции, которая реализует IEnumerable<T>
как обычный цикл foreach
. Реализация Parallel.Foreach
выполнит всю работу по распараллеливанию за вас:- разобьёт коллекцию на части,
- назначит и выполнит эти части в отдельных потоках.
| Method | Mean |Надо отметить, что если коллекции небольшие и время выполнения одной итерации быстрое, переход с
|-------------------- |-----------:|
| ForeachTest | 1,543.9 ms |
| ParallelForeachTest | 199.9 ms |
foreach
на Parallel.Foreach
может даже ухудшить производительность, особенно если используется синхронизация потоков из-за доступа к общим ресурсам.Функциональные возможности, предоставляемые классом Parallel, являются отличным выбором для длительных итераций, которые не зависят друг от друга и не требуют использования примитивов синхронизации, таких как потокобезопасные коллекции.
Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da
День восемьсот сорок третий. #PerformanceTips
5 Способов Повысить Производительность Кода C# Бесплатно
4. Избегайте неявного линейного поиска
Линейный поиск - это один из простейших алгоритмов поиска, который перебирает все элементы коллекции один за другим, пока не будет найден указанный элемент.
Хотя разработчики обычно не реализуют алгоритм поиска явно, линейный поиск всё же часто вызывает снижение производительности.
Разработчикам не обязательно знать, как реализован каждый из методов LINQ. Важно знать основы: в .NET коллекция
Решением этой конкретной проблемы было бы использование структуры данных, подходящей для конкретной задачи. В нашем случае у нас есть идентификаторы, которые всегда уникальны. Это позволяет нам преобразовать коллекцию в
Разработчики выиграют от преобразования в
Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da
5 Способов Повысить Производительность Кода C# Бесплатно
4. Избегайте неявного линейного поиска
Линейный поиск - это один из простейших алгоритмов поиска, который перебирает все элементы коллекции один за другим, пока не будет найден указанный элемент.
Хотя разработчики обычно не реализуют алгоритм поиска явно, линейный поиск всё же часто вызывает снижение производительности.
public void LinearSearchTest()В этом примере метод Any использует алгоритм линейного поиска, чтобы проверить, что указанный идентификатор находится в коллекции.
{
var ids = Enumerable.Range(0, 10000000);
int idToFind = 9193513;
var exists = ids.Any(u => u == idToFind);
}
Разработчикам не обязательно знать, как реализован каждый из методов LINQ. Важно знать основы: в .NET коллекция
List<T>
базируется на массиве. А когда дело доходит до поиска значения в несортированном массиве, его сложность составляет O(n). Независимо от того, какой метод LINQ используется для поиска значения в массиве (Any
, Contains
или Where
), сложность остается прежней.Решением этой конкретной проблемы было бы использование структуры данных, подходящей для конкретной задачи. В нашем случае у нас есть идентификаторы, которые всегда уникальны. Это позволяет нам преобразовать коллекцию в
HashSet<T>
.public void HashSetTest()Погодите-ка.
{
var ids = Enumerable.Range(0, 10000000).ToHashSet();
int idToFind = 9193513;
var exists = ids.Contains(idToFind);
}
| Method | Mean |
|----------------- |----------:|
| LinearSearchTest | 63.74 ms |
| HashSetTest | 334.34 ms |
HashSetTest
оказался гораздо медленнее, чем LinearSearchTest
. Это связано с тем, что время тратится и на создание коллекции HashSet<T>
, что является трудоёмкой операцией для больших наборов.Разработчики выиграют от преобразования в
HashSet
только в том случае, если они планируют часто вызывать на коллекции метод Contains. Если вынести создание коллекции из тестов производительности и измерить только время нахождения элемента, результаты будут кардинально отличаться. Поиск значения в HashSet<T>
почти не занимает времени по сравнению с поиском значения в коллекции List<T>
.Продолжение следует…
Источник: https://levelup.gitconnected.com/5-ways-to-improve-the-performance-of-c-code-for-free-c89188eba5da