Procesamiento de grandes datos en C#: desde LINQ hasta flujos paralelos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué C# es su herramienta secreta para Big Data?

\n\n

Cuando se habla de procesamiento de grandes datos, lo primero que viene a la mente son Python con sus Pandas, Scala con Spark o Java con Hadoop. Pero seamos honestos: C# a menudo queda injustamente en la sombra. Y es una lástima. La plataforma .NET nos brinda herramientas tan potentes como PLINQ, MemoryMappedFile, Parallel.For y flujos asíncronos, que permiten procesar gigabytes de datos en hardware común sin complicaciones.

\n\n

En este artículo no divagaremos con teoría — pasaremos directamente a la acción. Aprenderá cómo sacar el máximo provecho de su procesador al procesar grandes datos en C#, cómo evitar caer en OutOfMemoryException y cómo manejar archivos de decenas de gigabytes como si fueran un libro grande.

\n\n

El problema principal: ¿cómo no matar la memoria?

\n\n

Imagine: tiene un archivo CSV de 50 GB con registros del servidor. Lo abre con un StreamReader normal y lo lee línea por línea. ¿Todo bien? No. Si intenta cargarlo completamente en un List<string> — hola, OutOfMemoryException. Incluso si tiene 64 GB de RAM, .NET puede fragmentar la memoria de tal manera que un objeto grande simplemente no quepa en el montón.

\n\n

Solución número uno: streaming y fragmentación en chunks. No cargue todo de una vez — mastique por partes.

\n\n
public async IAsyncEnumerable<string[]> ReadCsvChunksAsync(string filePath, int chunkSize = 10000)\n{\n    using var reader = new StreamReader(filePath);\n    string? line;\n    var buffer = new List<string>(chunkSize);\n\n    while ((line = await reader.ReadLineAsync()) != null)\n    {\n        buffer.Add(line);\n        if (buffer.Count >= chunkSize)\n        {\n            yield return buffer.ToArray();\n            buffer.Clear();\n        }\n    }\n\n    if (buffer.Count > 0)\n        yield return buffer.ToArray();\n}\n
\n\n

Ahora puede procesar gigabytes sin preocuparse por la memoria:

\n\n
await foreach (var chunk in ReadCsvChunksAsync("logs.csv"))\n{\n    ProcessChunk(chunk); // su lógica\n}\n
\n\n

Procesamiento paralelo: usando todos los núcleos del procesador

\n\n

Bien, leemos datos en porciones. Pero si tiene un procesador de 16 núcleos y los datos se procesan secuencialmente — está usando solo el 6% de su capacidad. Es hora de activar la computación paralela en C#.

\n\n

PLINQ — Parallel LINQ

\n\n

La magia más simple es agregar .AsParallel() a su consulta LINQ. Pero con grandes datos hay que tener cuidado: PLINQ decide por sí mismo cuándo paralelizar, y esto puede provocar una fragmentación excesiva.

\n\n
var numbers = Enumerable.Range(1, 10_000_000);\n\n// LINQ normal (lento)\nvar sum = numbers.Where(n => n % 2 == 0).Sum();\n\n// PLINQ (rápido, pero con cuidado)\nvar parallelSum = numbers\n    .AsParallel()\n    .WithDegreeOfParallelism(Environment.ProcessorCount)\n    .Where(n => n % 2 == 0)\n    .Sum();\n\nConsole.WriteLine($"Resultado: {parallelSum}");\n
\n\n

Pero PLINQ no es una panacea. Para el procesamiento complejo de grandes datos en C#, es mejor usar Parallel.ForEach con control manual de la división.

\n\n

Parallel.ForEach con división en rangos

\n\n
var allLines = File.ReadLines("huge_dataset.csv");\nvar processedCount = 0;\nvar lockObject = new object();\n\nParallel.ForEach(\n    allLines,\n    new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },\n    line =>\n    {\n        var result = ProcessLine(line); // su función pesada\n        lock (lockObject)\n        {\n            processedCount++;\n            if (processedCount % 100_000 == 0)\n                Console.WriteLine($"Procesados: {processedCount}");\n        }\n    });\n
\n\n

MemoryMappedFile: trabajando con archivos como si fueran memoria RAM

\n\n

Cuando el archivo es demasiado grande para cargarlo en RAM, pero necesita

Blogs

Book Recommendations