I'm having some trouble finding a way to properly parallelize the processing of an IEnumerable
, where the actual generation of each item takes a considerable amount of time, so it effectively locks for a bit every call to MoveNext
on the reader side.
This is my scenario:
I have a method that takes an
IEnumerable<(float[], float[])>
(the specific type doesn't actually matter here), and I need to compute those items, split them into batches of a fixed side, then process every batch.
Assume I already have the partition code ready (see this answer here) as well as the code to process each individual partition.
The problem is that, as I've said, yielding every value from the initial list involves some IO/CPU operations (one would typically read an image, process it and return those two matrices with), so even with:
var items = dataset.AsParallel().Partition(size).ToArray().AsParallel().Select(partition =>
{
// Process the partitions here..
return partition;
}).ToArray(); // Two AsParallel calls because I'm doing two selections one after the other
I get around 25% CPU usage (I have an 8-cores AMD FX-8350), because I guess it's the actual generation of the items in the first list that causes the enumeration to be slow, before even getting to the first AsParallel
call.
I was thinking a possible solution would be to require the user of this method to instead provide an IEnumerable<Func<(float[], float[])>>
, as that would allow my method to easily process those elements in parallel.
My question is: is this the only possible solution, or is there another way to enumerate a "locking" IEnumerable
in parallel, without having this slowdown causing to each item being yielded not in parallel?
Thanks!
Edit: to clarify, I am not writing the actual code in the first IEnumerable
, that's up to the user of the library in question, which will input its own IEnumerable
for the library to split into batches and work on.
One of the reasons why I was hoping there'd be an alternative to a Func
delegate was because, on the user side, just returning a tuple would be easier and more intuitive than having to explicitly return a function that lazily computes the whole thing.