c#-使用Parallel.For时是否有一种方法可以控制空间/内存位置

我有大量未缩放的浮点数-数组长度为40,000,000.为了扩展此数组,我认为使用Parallel.For()会更有效.这是用于缩放数据的for循环的顺序版本:for (i = 0; i rawData.Length; i++){scaledData[i] = rawData[i] * scal...

我有大量未缩放的浮点数-数组长度为40,000,000.为了扩展此数组,我认为使用Parallel.For()会更有效.这是用于缩放数据的for循环的顺序版本:

for (i = 0; i < rawData.Length; i++)
{
    scaledData[i] = rawData[i] * scale + offset;
}

这是转换为使用Parallel.For()的代码,例如:

Parallel.For(0, rawData.Length, i => {
    scaledData[i] = rawData[i] * scale + offset;
});

但是性能更差!基于观察索引/线程组合,我的猜测是Parallel.For()正在以导致过多分页的方式访问内存.为了验证这一理论,我尝试使用Parallel.Invoke()像这样:

Parallel.Invoke(
    () => { for (int i =        0; i < 10000000; i++) { dst[i] = src[i] * scale + offset; } },
    () => { for (int i = 10000000; i < 20000000; i++) { dst[i] = src[i] * scale + offset; } },
    () => { for (int i = 20000000; i < 30000000; i++) { dst[i] = src[i] * scale + offset; } },
    () => { for (int i = 30000000; i < 40000000; i++) { dst[i] = src[i] * scale + offset; } },
);

这样做的效果明显更好,但是我讨厌这段代码的硬编码性质.我有4个处理器,这就是为什么有4个动作传递给Invoke()的原因.

有没有办法让Parallel.For()以不会破坏内存的方式将索引分配给线程?

解决方法:

您可以使用自定义分区程序来获得所需的行为,而不必诉诸使用Parallel.Invoke. RangPartitioner是您要开始的.

    var rangePartitioner = Partitioner.Create(0, rawData.Length);

    double[] results = new double[rawData.Length];

    Parallel.ForEach(rangePartitioner, (range, loopState) =>
    {
        for (int i = range.Item1; i < range.Item2; i++)
        {
            scaledData[i] = rawData[i] * scale * offset;
        }
    });

您可以创建一个自定义分区程序并使GetPartition() method重载,以将块大小调整为适合您的需求.

有关详细讨论,请参见Custom Partitioners for PLINQ and TPL.

是的,这会改善数据的局部性吗?前提是您的数组包含值类型.在这种情况下,它们将被分配为连续内存块.对于引用类型,情况并非如此. FWIW我试图通过OK来改善这样的内存局部性,但并没有令人惊讶的改进.我得出的结论是,CLR可能还会有许多其他的内存访问,这可能会使您很难理解最终的内存访问模式.

本文标题为:c#-使用Parallel.For时是否有一种方法可以控制空间/内存位置

基础教程推荐