【问题标题】:Using shared variable in Parallel For在 Parallel For 中使用共享变量
【发布时间】:2017-04-28 23:29:14
【问题描述】:

我正在努力在下面的代码中使用 Parallel.For 而不是 for 循环。 由于 CoefficientVector 向量数组的大小相当大,所以对我来说只重置数组元素值而不是为每次迭代创建新值是有意义的。

我尝试用 Parallel For 替换外循环;并假设并行的每个分区由一个单独的线程运行,将拥有自己的 CoefficientVector 类的副本,因此对我来说,为每个线程拥有一个 CoefficientVector 对象的实例并重置向量元素而不是重新创建数组。我虽然发现很难在 Parallel For 上进行这种优化(?)。谁能帮忙。

 static void Main(string[] args)
    {
        System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
        timer.Start(); 

        int numIterations = 20000;
        int numCalpoints = 5000;
        int vecSize = 10000;

        CalcPoint[] calcpoints = new CalcPoint[numCalpoints];

        CoefficientVector coeff = new CoefficientVector();
        coeff.vectors = new Vector[vecSize];

        //not sure how to correctly use Parallel.For here
        //Parallel.For(0, numCalpoints, =>){
        for (int i = 0; i < numCalpoints;i++)
        {
            CalcPoint cp = calcpoints[i];

            //coeff.vectors = new Vector[vecSize];
            coeff.ResetVectors();

            //doing some operation on the matrix n times
            for (int n = 0; n < numIterations; n++)
            {
                coeff.vectors[n].x += n;
                coeff.vectors[n].y += n;
                coeff.vectors[n].z += n;
            }

            cp.result = coeff.GetResults();
        }

        Console.Write(timer.Elapsed);
        Console.Read();
    }
}

class CoefficientVector
{
    public Vector[] vectors;

    public void ResetVectors()
    {
        for (int i = 0; i < vectors.Length; i++)
        {
            vectors[i].x = vectors[i].y = vectors[i].z = 0;
        }
    }

    public double GetResults()
    {
        double result = 0;
        for (int i = 0; i < vectors.Length; i++)
        {
            result += vectors[i].x * vectors[i].y * vectors[i].z;
        }
        return result;
    }
}

struct Vector
{
    public double x;
    public double y;
    public double z;
}

struct CalcPoint
{
    public double result;
}

【问题讨论】:

  • 如果你想重置向量数组中的向量,为什么不使用Array.Clear(...)? Array.Clear(...) 将每个数组元素设置为其默认值。这在这里可以工作,因为结构是值类型,并且将 Vector 结构设置为其默认值意味着将其每个字段设置为其默认值(对于数字类型为零)...
  • 谢谢@elgonzo。也许我没有正确解释它。但是每个计算点都需要保持自己的结果。使用 Parallel For 和 Array.Clear(...) 将改变其他计算点的结果。这是正确的吗?
  • 抱歉,误解了您的问题。只需使用 Parallel.Foreach 编写代码。不要担心优化。如果您觉得即使使用 Parallel.Foreach 代码仍然太慢,请分析 Parallel.Foreach 执行的代码以查看进一步优化工作的回报。 (我假设您在这里给出的代码有些简化,实际矩阵计算要复杂一些)
  • @elgonzo。我喜欢你在认真对待这种优化时表现得很冷静:)
  • @elgonzo 正如你所说。计算实际上是使用矩阵系数进行光线追踪,耗时耗力。

标签: c# parallel.foreach


【解决方案1】:

Parallel.For 方法目前有 12 个overloads。除了int、long、ParallelOptions 和ParallelState 动作参数的变体之外,您还可以注意到有几个具有额外的通用参数TLocal,例如this:

public static ParallelLoopResult For<TLocal>(
    int fromInclusive,
    int toExclusive,
    Func<TLocal> localInit,
    Func<int, ParallelLoopState, TLocal, TLocal> body,
    Action<TLocal> localFinally
)

使用线程本地数据执行 for 循环,其中迭代可以并行运行,并且可以监视和操作循环的状态。

换句话说,TLocal 允许您分配、使用和释放一些线程本地状态,即您所需要的(TLocal 将是您每个线程的 CoefficientVector 实例)。

因此您可以删除 coeff 局部变量并像这样使用上述重载:

CalcPoint[] calcpoints = new CalcPoint[numCalpoints];

Parallel.For(0, numCalpoints,
    () => new CoefficientVector { vectors = new Vector[vecSize] }, // localInit
    (i, loopState, coeff) => // body
    {
        coeff.ResetVectors();

        //doing some operation on the matrix
        for (int n = 0; n < coeff.vectors.Length; n++)
        {
            coeff.vectors[n].x += n;
            coeff.vectors[n].y += n;
            coeff.vectors[n].z += n;
        }

        calcpoints[i].result = coeff.GetResults();

        return coeff; // required by the body Func signature
    },
    coeff => { } // required by the overload, do nothing in this case
);

【讨论】:

  • 那是美丽的。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-08-14
  • 2014-04-20
  • 2018-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-20
相关资源
最近更新 更多