LINQ, dal profiler al compilatore
Quattro errori trovati in produzione, i benchmark, e cosa genera davvero il compilatore
LINQ è dichiarativo, ma l'esecuzione è imperativa: la stessa sintassi nasconde costi radicalmente diversi a seconda della struttura dati sotto. La serie parte da un audit reale su un dispatcher di flotta con un budget di latenza sotto i 100ms, mette i numeri con BenchmarkDotNet, e scende fino alla state machine che il compilatore genera.
Lo stesso codice, due ordini di grandezza di differenza
.Contains() su una List è O(n). .Contains() su un HashSet è O(1). Il codice che scrivete è identico. Il profiler racconta due storie diverse.
È il filo di tutta la serie: LINQ è dichiarativo, ma l'esecuzione è imperativa, e la stessa sintassi nasconde costi radicalmente diversi a seconda della struttura dati sotto. Il punto di partenza è un audit vero, su un dispatcher per una flotta di veicoli commerciali che tiene tutto in memoria per stare sotto i 100ms di latenza: quattro pattern trovati in produzione, e il più costoso faceva 1.400.000 confronti dove ne bastavano 2000.
Da li' la serie scende: i numeri misurati con BenchmarkDotNet invece che stimati, la state machine che il compilatore genera dietro una Where(), e infine come rendere visibile l'esecuzione con il tracing invece di doverla leggere nel codice.
Cosa imparerai
- ✓ Riconoscere i quattro pattern che trasformano operazioni lineari in quadratiche
- ✓ Misurare con BenchmarkDotNet invece di indovinare dove sta il costo
- ✓ Capire cosa genera il compilatore dietro una Where(), e perché cambia il conto
- ✓ Rendere visibile l'esecuzione con tracing, invece di leggerla nel codice
Articoli della serie
- 014 Errori LINQ che Ho Trovato in Produzione (e Come Costavano 1000x) 17 min
Quattro pattern LINQ che trasformano operazioni lineari in quadratiche, da un audit su un dispatcher di flotta con budget di latenza sotto i 100ms.
→ - 02Quanto Costa Davvero LINQ? Benchmark su .NET 8 con i Numeri 14 min
Benchmark con BenchmarkDotNet su .NET 8: List contro HashSet fino a 63x, pipeline lazy con il 60% di allocazioni in meno, e il costo fisso di AsParallel.
→ - 03Cosa Genera il Compilatore Quando Scrivi una Where() — State Machine, Iteratori e IL 14 min
Dietro ogni Where() c'è una state machine generata dal compilatore. In .NET 8 Enumerable.Where usa iteratori specializzati, e un cast li disattiva.
→ - 04Vedere LINQ in Azione: Tracing con OpenTelemetry e Grafana Tempo 16 min
Rendere visibile l'esecuzione di LINQ con OpenTelemetry e Grafana Tempo: la waterfall mostra lo streaming, e OrderBy nel mezzo lo spezza.
→