Комментарии 14
???
Parallel.ForEach(items.Distinct(), item =>{ Process(item);});
Это убирает только полностью одинаковые элементы внутри одного прохода и не решает задачи Last-Write-Wins по ключу, кэширования результатов между вызовами, адаптивного выбора стратегии выполнения и повторного использования уже вычисленных значений. Поэтому для простых коллекций Distinct() действительно может быть достаточен, а Principium.Parallel ориентирован на сценарии, где дубликаты определяются по ключу, данные приходят батчами, а стоимость вычислений достаточно высока, чтобы выигрыш от коалесинга и кэша был существенным.
Distinct() решает другую задачу — удаление одинаковых элементов в рамках одного перечисления. Principium.Parallel работает с произвольными объектами через keySelector, поддерживает Last-Write-Wins по ключу, кэширование между вызовами и адаптивно переключается между параллельным выполнением, коалесингом и кэшем в зависимости от уровня дубликатов. Поэтому это не альтернатива Distinct(), а решение для более широкого класса задач.
Это проприетарная ллм-либа, рождённая за пару дней, без адекватной архитектуры, надзора и ревью ллм-ного кода.
Там даже в простейшей документации косяки


<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Test_lib_neuroslop</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
error: NU1202: Package Principium.Parallel 1.0.1 is not compatible with net6.0 (.NETCoreApp,Version=v6.0). Package Principium.Parallel 1.0.1 supports: net8.0 (.NETCoreApp,Version=v8.0)В либе обвязка под лицензию и чуток простеньких абстракций, которые зачем-то оборачивают самый стандартный дотнет.
Фактически там просто обёртка над ConcurrentDictionary и Dictionary с смешными(с точки зрения производительности) штуками под капотом.
Но проблема возникает тогда, когда внутри коллекции появляются дубликаты.
Garbage in - garbage out.
Не всегда. Во многих реальных системах дубликаты — это не ошибка данных, а нормальная часть бизнес-процесса. Например, события телеметрии, обновления состояния сущностей, сообщения из очередей, ETL-пайплайны, CDC-потоки из БД, пересчёт эмбеддингов или агрегация логов. Там один и тот же ключ может встречаться десятки или сотни раз в одном батче, и задача как раз состоит в том, чтобы эффективно обработать такие данные. Principium.Parallel не исправляет "плохие данные", а оптимизирует типичный сценарий, когда повторения ключей являются ожидаемым свойством входного потока.
И что предлагаете? Делать фильтрованную копию многогиговой базы, чтобы по ней прошвырнуться?
Нет, речь не про создание копии или предварительную “чистку” многогиговой базы. В большинстве таких сценариев данные уже приходят батчами или потоками, и повторения неизбежны из-за природы доставки (ретраи, at-least-once семантика, повторные события, обновления состояния). Поэтому задача не в том, чтобы заранее построить идеальный набор данных, а в том, чтобы при обработке не выполнять одинаковую тяжёлую работу повторно для одного и того же ключа. Principium.Parallel работает именно на этом уровне — он коалесит вычисления, кэширует результат и переиспользует его внутри и между проходами, не требуя предварительного копирования или фильтрации исходного массива.
П.С. вроде не на свой вопрос ответ написал, но всё-равно по сути библиотеки, пусть остаётся
В очереди постоянно встречаются повторяющиеся события:
...
В большинстве случаев интересует только последнее состояние.
Ну так сделайте нормальную очередь.
Очередь отвечает за доставку событий, а не за их семантическое слияние. Повторные события возникают из-за ретраев, at-least-once доставки, ребалансировки партиций, повторной публикации продьюсером, сетевых таймаутов и восстановления после сбоев — и это считается нормальной моделью работы Kafka/RabbitMQ/Azure Service Bus. Поэтому “последнее состояние” — это уже задача потребителя, а не очереди. Именно там и появляется необходимость в LWW-логике, коалесинге и кэшировании, которые и закрывает Principium.Parallel: он не заменяет очередь, а обрабатывает неизбежную реальность дубликатов в потоках данных.
Ох, блин, прочитал заголовок, думал что-то новое и интересное, а тут нейрослоп пишет про редкий сценарий где кэш поможет. Зачем в заголовке было писать «ваш Parallel.ForEach впустую сжигает CPU»? Мой ниче не сжигает, я не идиот и там где очевидно что кэш поможет - использую его.

Почему ваш Parallel.ForEach впустую сжигает CPU — ускоряем обработку данных до 600+ раз