.net 可以并行化使用Yield的迭代器吗?

.net 可以并行化使用Yield的迭代器吗?,.net,parallel.foreach,parallel-extensions,.net,Parallel.foreach,Parallel Extensions,考虑以下代码,它扩展了数组.NET类型: Public Module ArrayExtensions <System.Runtime.CompilerServices.Extension> Public Iterator Function ToEnumerable(Of T)(target As Array) As IEnumerable(Of T) For Each item In target Yield DirectCas

考虑以下代码,它扩展了数组.NET类型:

Public Module ArrayExtensions
    <System.Runtime.CompilerServices.Extension>
    Public Iterator Function ToEnumerable(Of T)(target As Array) As IEnumerable(Of T)
        For Each item In target
            Yield DirectCast(item, T)
        Next
    End Function
End Module
其中,
dataarray
是(在本例中)ConcurrentDictionary
loadedData
中的值项,类型为
Single(,)

如果没有当前编写的
ToEnumerable
,则没有可供
Max()
扩展函数挂钩的IEnumerable接口

“并行化”这个函数需要什么?没有任何形式的
Parallel.For
我已经尝试过了,因为
loadedData
数组没有被识别为
IEnumerable
类型。(这可能是因为单个(,)被处理为值类型?)


(没有答案必须使用VB.C#也可以!)

因为您已经有了
IEnumerable
,所以您可以在上面使用
AsParallel()
(例如
dataArray.ToEnumerable().AsParallel().Min()
)。但是
IEnumerable
接口本质上是串行的,您可以并行处理它的元素,但不能迭代它。这意味着对于非常简单的操作,如
Min()
,这种并行化没有多大意义

这里有意义的是并行化迭代。这是可能的,因为您可以使用索引器访问数组的特定项

我试着用一个软件来做这件事,但是结果比串行版本更糟。问题是每次迭代的开销必须尽可能小,而直接使用分区器很难做到这一点

相反,您可以做的是只对数组的第一个维度进行分区(假设您可以确保它至少与您的CPU数量一样大),然后使用只返回第一个维度一部分的
ToEnumerable()
。比如:

private static IEnumerable<T> ToEnumerable<T>(this T[,,] array, int from, int to)
{
    for (int i = from; i < to; i++)
    {
        for (int j = 0; j < array.GetLength(1); j++)
        {
            for (int k = 0; k < array.GetLength(2); k++)
            {
                yield return array[i, j, k];
            }
        }
    }
}

Partitioner.Create(0, data.GetLength(0))
           .AsParallel()
           .Select(range => data.ToEnumerable(range.Item1, range.Item2).Min())
           .Min()
总而言之,我的计算机上有一个使用各种方法的计时表:

  • 3D阵列,
    ToEnumerable
    ,序列号:18.6秒
  • 3D阵列,
    ToEnumerable
    ,PLINQ:7.4秒
  • 3D阵列,手动循环,串行:4.0秒
  • 3D阵列,手动循环,
    并行。ForEach
    :1.1s
  • 锯齿阵列,手动循环,串行:2.4秒
  • 锯齿阵列,手动环路,PLINQ:0.8秒

我认为您根本不需要这种方法。你可以用。看来你是发明了我可能有的
Cast
method,但这不是问题所在。问题是:如何并行化?所以你想在不使用并行API的情况下并行化它,是吗?@RobPerkins,关于
ParallelEnumerable.Cast
?@RobPerkins,不是,但是你可以使它与
AsParallel()
并行化。。。您的代码将变成
target.AsParallel().Cast()
目前我对多维数组非常着迷,但是您的其他方法给了我一些想法,谢谢!你觉得这个
Enqueue()
函数有多快?如果它在最里面的循环中有一个位置,并且存储了4个元组的
(i,j,k,value)
,那么我就有办法在别处做一些我需要的稀疏数组工作。@Robberkins如果它是稀疏数组,那么我认为它应该足够快。但是你必须自己测量。斯维克,到目前为止,我的特定数据集的计时非常好。然而,
OrderablePartitioner
正在为每个分区中的数组中的两个项创建一个分区,这不是我所期望的。除非这在两个6核超线程CPU上是正常的?@RobPerkins你是说
paritizer.Create()
?这取决于你的第一维度有多大。在我的四核上,它似乎总是创建大约10-15个分区。但是如果您不喜欢,可以使用它来指定分区的大小。
private static IEnumerable<T> ToEnumerable<T>(this T[,,] array, int from, int to)
{
    for (int i = from; i < to; i++)
    {
        for (int j = 0; j < array.GetLength(1); j++)
        {
            for (int k = 0; k < array.GetLength(2); k++)
            {
                yield return array[i, j, k];
            }
        }
    }
}

Partitioner.Create(0, data.GetLength(0))
           .AsParallel()
           .Select(range => data.ToEnumerable(range.Item1, range.Item2).Min())
           .Min()
var length0 = data.GetLength(0);
var length1 = data.GetLength(1);
var length2 = data.GetLength(2);

float min = float.MaxValue;

for (int i = 0; i < length0; i++)
{
    for (int j = 0; j < length1; j++)
    {
        for (int k = 0; k < length2; k++)
        {
            float value = data[i, j, k];
            if (value < min)
                min = value;
        }
    }
}

return min;
var results = new ConcurrentQueue<float>();
var length1 = data.GetLength(1);
var length2 = data.GetLength(2);

Parallel.ForEach(
    Partitioner.Create(0, data.GetLength(0)), range =>
    {
        float min = float.MaxValue;

        for (int i = range.Item1; i < range.Item2; i++)
        {
            for (int j = 0; j < length1; j++)
            {
                for (int k = 0; k < length2; k++)
                {
                    float value = data[i, j, k];
                    if (value < min)
                        min = value;
                }
            }
        }

        results.Enqueue(min);
    });

return results.Min();
dataJagged.AsParallel().Min(
    level1 =>
    {
        float min = float.MaxValue;

        foreach (var level2 in level1)
        {
            for (int k = 0; k < level2.Length; k++)
            {
                float value = level2[k];
                if (value < min)
                    min = value;
            }
        }

        return min;
    });