Performance e Otimização para .NET Senior
📋 Conceitos Fundamentais
Por que Performance é Crítica?
- User Experience: Tempo de resposta afeta diretamente a satisfação do usuário
- Scalability: Sistemas otimizados suportam mais carga
- Cost Efficiency: Menor uso de recursos = menor custo
- Competitive Advantage: Performance pode ser um diferencial
Métricas de Performance
- Response Time: Tempo de resposta das requisições
- Throughput: Número de requisições por segundo
- Memory Usage: Uso de memória e garbage collection
- CPU Usage: Utilização do processador
- I/O Operations: Operações de entrada/saída
🚀 Otimizações de Código
1. Memory Management
Boxing e Unboxing
csharp
// ❌ Evitar - Boxing desnecessário
public void ProcessData(object data)
{
if (data is int value)
{
ProcessInt(value); // Unboxing
}
}
// ✅ Melhor - Usar generics
public void ProcessData<T>(T data)
{
if (data is int value)
{
ProcessInt(value); // Sem boxing
}
}
// ✅ Melhor ainda - Overload específico
public void ProcessData(int data)
{
ProcessInt(data); // Sem boxing
}String Operations
csharp
// ❌ Evitar - Concatenação ineficiente
public string BuildMessage(string[] parts)
{
string result = "";
foreach (var part in parts)
{
result += part + " "; // Cria nova string a cada iteração
}
return result;
}
// ✅ Melhor - StringBuilder
public string BuildMessage(string[] parts)
{
var sb = new StringBuilder();
foreach (var part in parts)
{
sb.Append(part).Append(" ");
}
return sb.ToString();
}
// ✅ Melhor ainda - String.Join
public string BuildMessage(string[] parts)
{
return string.Join(" ", parts);
}Collections Performance
csharp
// ❌ Evitar - Lista sem tamanho inicial
public List<int> CreateList()
{
var list = new List<int>(); // Capacidade inicial = 0
for (int i = 0; i < 1000; i++)
{
list.Add(i); // Redimensiona várias vezes
}
return list;
}
// ✅ Melhor - Especificar capacidade inicial
public List<int> CreateList()
{
var list = new List<int>(1000); // Capacidade inicial = 1000
for (int i = 0; i < 1000; i++)
{
list.Add(i); // Sem redimensionamento
}
return list;
}
// ✅ Melhor ainda - Array se tamanho fixo
public int[] CreateArray()
{
var array = new int[1000];
for (int i = 0; i < 1000; i++)
{
array[i] = i; // Mais rápido que List
}
return array;
}2. LINQ Otimizações
Eager vs Lazy Evaluation
csharp
// ❌ Evitar - Múltiplas iterações
public List<string> GetFilteredNames(List<Person> people)
{
var filtered = people.Where(p => p.Age > 18); // Lazy
var names = filtered.Select(p => p.Name); // Lazy
return names.ToList(); // Executa ambas as queries
}
// ✅ Melhor - Single iteration
public List<string> GetFilteredNames(List<Person> people)
{
var result = new List<string>();
foreach (var person in people)
{
if (person.Age > 18)
{
result.Add(person.Name);
}
}
return result;
}
// ✅ Melhor ainda - LINQ otimizado
public List<string> GetFilteredNames(List<Person> people)
{
return people.Where(p => p.Age > 18)
.Select(p => p.Name)
.ToList(); // Single iteration
}Compiled Queries
csharp
// ❌ Evitar - Query recompilada a cada execução
public async Task<List<Order>> GetOrdersByCustomer(int customerId)
{
return await _context.Orders
.Where(o => o.CustomerId == customerId)
.ToListAsync();
}
// ✅ Melhor - Compiled Query
private static readonly Func<ApplicationDbContext, int, Task<List<Order>>>
GetOrdersByCustomerCompiled =
EF.CompileAsyncQuery((ApplicationDbContext context, int customerId) =>
context.Orders.Where(o => o.CustomerId == customerId));
public async Task<List<Order>> GetOrdersByCustomer(int customerId)
{
return await GetOrdersByCustomerCompiled(_context, customerId);
}3. Async/Await Patterns
ConfigureAwait
csharp
// ❌ Evitar - Pode causar deadlock em alguns cenários
public async Task<string> GetDataAsync()
{
var result = await _httpClient.GetStringAsync("http://api.example.com/data");
return result;
}
// ✅ Melhor - Usar ConfigureAwait(false)
public async Task<string> GetDataAsync()
{
var result = await _httpClient.GetStringAsync("http://api.example.com/data")
.ConfigureAwait(false);
return result;
}Cancellation Tokens
csharp
// ✅ Boa prática - Suportar cancellation
public async Task<List<Order>> GetOrdersAsync(CancellationToken cancellationToken = default)
{
return await _context.Orders
.Where(o => o.Status == OrderStatus.Pending)
.ToListAsync(cancellationToken);
}
// ✅ Uso com timeout
public async Task<List<Order>> GetOrdersWithTimeoutAsync()
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
return await GetOrdersAsync(cts.Token);
}🗄️ Database Performance
1. Query Optimization
Índices
sql
-- ✅ Índice composto para queries complexas
CREATE INDEX IX_Orders_CustomerId_Status_CreatedDate
ON Orders (CustomerId, Status, CreatedDate);
-- ✅ Índice incluindo colunas frequentemente selecionadas
CREATE INDEX IX_Orders_CustomerId_Include_TotalAmount
ON Orders (CustomerId)
INCLUDE (TotalAmount, Status);Query Patterns
csharp
// ❌ Evitar - N+1 Query Problem
public async Task<List<OrderDto>> GetOrdersWithCustomerInfo()
{
var orders = await _context.Orders.ToListAsync();
var result = new List<OrderDto>();
foreach (var order in orders)
{
var customer = await _context.Customers.FindAsync(order.CustomerId); // N+1
result.Add(new OrderDto { Order = order, Customer = customer });
}
return result;
}
// ✅ Melhor - Include para eager loading
public async Task<List<OrderDto>> GetOrdersWithCustomerInfo()
{
var orders = await _context.Orders
.Include(o => o.Customer)
.Select(o => new OrderDto
{
Order = o,
Customer = o.Customer
})
.ToListAsync();
return orders;
}2. Connection Pooling
csharp
// ✅ Configuração de connection pooling
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlOptions =>
{
sqlOptions.MaxBatchSize(100);
sqlOptions.EnableRetryOnFailure(
maxRetryCount: 3,
maxRetryDelay: TimeSpan.FromSeconds(30),
errorNumbersToAdd: null);
}));
}3. Caching Strategies
Memory Cache
csharp
public class CachedOrderService : IOrderService
{
private readonly IOrderService _orderService;
private readonly IMemoryCache _cache;
private readonly TimeSpan _cacheExpiration = TimeSpan.FromMinutes(10);
public async Task<Order> GetOrderAsync(int orderId)
{
var cacheKey = $"order:{orderId}";
if (_cache.TryGetValue(cacheKey, out Order cachedOrder))
{
return cachedOrder;
}
var order = await _orderService.GetOrderAsync(orderId);
var cacheOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(_cacheExpiration)
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
_cache.Set(cacheKey, order, cacheOptions);
return order;
}
}Distributed Cache (Redis)
csharp
public class RedisOrderService : IOrderService
{
private readonly IOrderService _orderService;
private readonly IDistributedCache _cache;
private readonly ILogger<RedisOrderService> _logger;
public async Task<Order> GetOrderAsync(int orderId)
{
var cacheKey = $"order:{orderId}";
var cachedData = await _cache.GetStringAsync(cacheKey);
if (!string.IsNullOrEmpty(cachedData))
{
return JsonSerializer.Deserialize<Order>(cachedData);
}
var order = await _orderService.GetOrderAsync(orderId);
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
SlidingExpiration = TimeSpan.FromMinutes(10)
};
await _cache.SetStringAsync(cacheKey, JsonSerializer.Serialize(order), options);
return order;
}
}🔧 Profiling e Benchmarking
1. BenchmarkDotNet
csharp
[SimpleJob]
[MemoryDiagnoser]
public class StringConcatenationBenchmark
{
private string[] _data;
[GlobalSetup]
public void Setup()
{
_data = Enumerable.Range(1, 1000)
.Select(i => $"Item{i}")
.ToArray();
}
[Benchmark]
public string StringConcatenation()
{
string result = "";
foreach (var item in _data)
{
result += item + " ";
}
return result;
}
[Benchmark]
public string StringBuilder()
{
var sb = new StringBuilder();
foreach (var item in _data)
{
sb.Append(item).Append(" ");
}
return sb.ToString();
}
[Benchmark]
public string StringJoin()
{
return string.Join(" ", _data);
}
}2. Performance Counters
csharp
public class PerformanceMonitor
{
private readonly PerformanceCounter _cpuCounter;
private readonly PerformanceCounter _memoryCounter;
public PerformanceMonitor()
{
_cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
_memoryCounter = new PerformanceCounter("Memory", "Available MBytes");
}
public float GetCpuUsage()
{
return _cpuCounter.NextValue();
}
public float GetAvailableMemory()
{
return _memoryCounter.NextValue();
}
}3. Application Insights Profiling
csharp
public class ProfiledService
{
private readonly TelemetryClient _telemetryClient;
public async Task<Order> ProcessOrderAsync(Order order)
{
using var operation = _telemetryClient.StartOperation<RequestTelemetry>("ProcessOrder");
try
{
// Simular processamento
await Task.Delay(100);
operation.Telemetry.Success = true;
return order;
}
catch (Exception ex)
{
operation.Telemetry.Success = false;
_telemetryClient.TrackException(ex);
throw;
}
}
}🚀 Otimizações de ASP.NET Core
1. Response Caching
csharp
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
[ResponseCache(Duration = 300, Location = ResponseCacheLocation.Any)]
public async Task<ActionResult<Product>> GetProduct(int id)
{
var product = await _productService.GetProductAsync(id);
return Ok(product);
}
[HttpGet("list")]
[ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "category", "page" })]
public async Task<ActionResult<List<Product>>> GetProducts(string category, int page = 1)
{
var products = await _productService.GetProductsAsync(category, page);
return Ok(products);
}
}2. Output Caching
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddOutputCache(options =>
{
options.AddBasePolicy(builder =>
builder.Expire(TimeSpan.FromMinutes(10)));
options.AddPolicy("ShortCache", builder =>
builder.Expire(TimeSpan.FromMinutes(1)));
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseOutputCache();
}
}
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
[OutputCache(PolicyName = "ShortCache")]
public async Task<ActionResult<Product>> GetProduct(int id)
{
var product = await _productService.GetProductAsync(id);
return Ok(product);
}
}3. Compression
csharp
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
}
}📊 Monitoring Performance
1. Custom Metrics
csharp
public class PerformanceMetrics
{
private readonly Counter _requestCounter;
private readonly Histogram _responseTimeHistogram;
private readonly Gauge _activeConnectionsGauge;
public PerformanceMetrics()
{
_requestCounter = Metrics.CreateCounter("http_requests_total", "Total HTTP requests");
_responseTimeHistogram = Metrics.CreateHistogram("http_request_duration_seconds",
"HTTP request duration");
_activeConnectionsGauge = Metrics.CreateGauge("active_connections",
"Number of active connections");
}
public void IncrementRequestCount(string method, string path, int statusCode)
{
_requestCounter.WithLabels(method, path, statusCode.ToString()).Inc();
}
public IDisposable MeasureResponseTime()
{
return _responseTimeHistogram.NewTimer();
}
public void SetActiveConnections(int count)
{
_activeConnectionsGauge.Set(count);
}
}2. Health Checks
csharp
public class PerformanceHealthCheck : IHealthCheck
{
private readonly PerformanceMetrics _metrics;
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,
CancellationToken cancellationToken = default)
{
var avgResponseTime = _metrics.GetAverageResponseTime();
if (avgResponseTime > TimeSpan.FromSeconds(2))
{
return HealthCheckResult.Degraded($"Average response time is {avgResponseTime.TotalMilliseconds}ms");
}
return HealthCheckResult.Healthy();
}
}🎯 Melhores Práticas
1. Código
- Use
StringBuilderpara concatenações múltiplas - Prefira
string.Joinpara arrays - Use
ConfigureAwait(false)em libraries - Implemente
IDisposablecorretamente - Use
Span<T>para operações de baixo nível
2. Database
- Crie índices apropriados
- Use
Includepara evitar N+1 queries - Implemente connection pooling
- Use compiled queries para queries frequentes
- Monitore query performance
3. Caching
- Cache dados frequentemente acessados
- Use cache distribuído para escalabilidade
- Implemente cache invalidation strategies
- Monitore cache hit rates
4. Monitoring
- Configure alertas para métricas críticas
- Use profiling para identificar bottlenecks
- Monitore garbage collection
- Track business metrics
5. Infrastructure
- Use load balancing
- Implemente auto-scaling
- Configure CDN para conteúdo estático
- Use compression para reduzir payload
📋 Checklist de Performance
✅ Código
- [ ] Otimizar loops e iterações
- [ ] Usar estruturas de dados apropriadas
- [ ] Implementar async/await corretamente
- [ ] Evitar boxing/unboxing desnecessário
- [ ] Otimizar string operations
✅ Database
- [ ] Criar índices apropriados
- [ ] Otimizar queries
- [ ] Implementar connection pooling
- [ ] Usar compiled queries
- [ ] Monitorar query performance
✅ Caching
- [ ] Implementar memory cache
- [ ] Configurar distributed cache
- [ ] Definir cache invalidation
- [ ] Monitorar cache hit rates
- [ ] Otimizar cache keys
✅ Infrastructure
- [ ] Configurar load balancing
- [ ] Implementar auto-scaling
- [ ] Configurar CDN
- [ ] Otimizar network latency
- [ ] Monitorar resource usage
✅ Monitoring
- [ ] Configurar performance counters
- [ ] Implementar custom metrics
- [ ] Configurar alertas
- [ ] Monitorar business metrics
- [ ] Implementar profiling