Как обработать файл с терабайтом слов, если данные не помещаются в памяти
Что проверяют
Думаете ли вы про ограничения, а не про синтаксис чтения: память, диск, число уникальных слов. Формулировка обычно неполная, и первым делом ждут уточнения, что нужно: частоты, топ-K, сортировка или удаление дублей. Следом спрашивают, что делать, если не помещается даже словарь уникальных слов.
Как отвечать
Чтение всегда потоковое: File.ReadLines отдаёт строки лениво, StreamReader открывают с увеличенным буфером через FileStreamOptions и FileOptions.SequentialScan. Быстрее всего System.IO.Pipelines и разбор по Span<byte> без строки на каждое слово.
Дальше зависит от задачи:
- частоты, словарь помещается:
Dictionary<string, long>, обновление черезCollectionsMarshal.GetValueRefOrAddDefault, чтобы не искать ключ дважды. С .NET 9GetAlternateLookup<ReadOnlySpan<char>>позволяет искать по срезу без аллокации строки; - словарь не помещается: разбиение по хешу. Слово пишется в один из N файлов по остатку от хеша, одинаковые слова оказываются в одном файле, а он уже влезает в память. Хеш нужен стабильный, например
XxHash64изSystem.IO.Hashing:string.GetHashCode()рандомизирован между запусками; - топ-K: по каждой части держите
PriorityQueue<TElement, TPriority>размером K, потом сливаете; - сортировка: внешняя сортировка слиянием, отсортированные в памяти куски пишутся на диск и сливаются;
- приближённо: Count-Min Sketch или HyperLogLog.
Назовите узкое место: терабайт при чтении около 500 МБ/с это больше получаса только на ввод. Параллелизм помогает на NVMe и при тяжёлом разборе, файл делят по смещениям с выравниванием на границу слова.
Где спотыкаются
Начинают с File.ReadAllText или ReadAllLines. На таком объёме это OutOfMemoryException, а если в файле нет переводов строк, то же случится и с ReadLine: весь файл окажется одной строкой.
Разбивают слова через line.Split(' '). Это массив и новая строка на каждое слово, а подсчёт ломается на двойных пробелах, табуляции и знаках препинания.
Верят, что async ускорит обработку. Асинхронность освобождает поток, но пропускную способность диска не увеличивает. На одном HDD параллельное чтение ещё и замедлит работу.