Расскажите про опыт оптимизации и интересные случаи
Что проверяют
Умение работать с производительностью в .NET по замерам. Просьба про интересный случай добавлена не для красоты: интервьюер хочет увидеть, как вы расследуете неочевидное, а не как переписываете цикл.
Как отвечать
Возьмите случай, где причина оказалась не там, где её искали, и ведите его по шагам: симптом, что подозревали, чем померили, что нашли, чем закончилось.
Инструменты: BenchmarkDotNet для микробенчмарков, счётчики и трассировка из набора dotnet-утилит, дамп процесса при утечке, профилировщик в среде разработки, план запроса на стороне базы.
Что чаще всего оказывается настоящей причиной:
- запрос из ORM, который тянет лишние строки или доделывает фильтрацию на клиенте; помогают проекция нужных полей, чтение без отслеживания изменений и подходящий индекс;
- ленивые связи и N+1 обращений вместо одного запроса;
- блокирующее ожидание асинхронного кода через
.Resultили.Wait(), истощение пула потоков и рост задержек под нагрузкой; - аллокации в горячем пути: склейка строк, лишние копии массивов, упаковка значимых типов.
StringBuilder,Span<T>и пулы дают выигрыш, но только там, где профиль это показал.
var rows = await db.Orders
.AsNoTracking()
.Where(o => o.CreatedAt >= from)
.Select(o => new { o.Id, o.Total })
.ToListAsync(ct);Где спотыкаются
Приносят историю с круглыми процентами и точными миллисекундами, но не могут сказать, чем мерили и при какой нагрузке. Одного уточняющего вопроса хватает, чтобы рассказ выглядел выученным.
Второе: Task.Run как универсальный ускоритель. Он не ускоряет работу с сетью и диском, а переносит ожидание в пул потоков. В связке с .Result это ещё и надёжный способ получить взаимоблокировку.
Третье: правят код, когда время уходит в базу и сеть. Симптом простой: процессор процесса почти свободен, а задержка высокая. Пока это не разделено, любые изменения в C# двигают проценты, которых там почти нет.