首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何修复并行for()-loop以不返回与串行-loop()-loop不同的值?

如何修复并行for()-loop以不返回与串行-loop()-loop不同的值?
EN

Stack Overflow用户
提问于 2017-11-12 20:33:11
回答 2查看 322关注 0票数 0

下面的代码有问题。代码没有错误,但是当使用并行for循环和常规for循环时,我收到不同的输出值。我需要使并行for循环正常工作,因为我运行了这段代码数千次。有人知道为什么我的并行for循环返回不同的输出吗?

代码语言:javascript
复制
private object _lock = new object();

public double CalculatePredictedRSquared()
    {
        double press = 0, tss = 0, press2 = 0, press1 = 0;
        Vector<double> output = CreateVector.Dense(Enumerable.Range(0, 400).Select(i => Convert.ToDouble(i)).ToArray());
        List<double> input1 = new List<double>(Enumerable.Range(0, 400).Select(i => Convert.ToDouble(i)));
        List<double> input2 = new List<double>(Enumerable.Range(200, 400).Select(i => Convert.ToDouble(i)));

            Parallel.For(0, output.Count, i =>
            {
                ConcurrentBag<MultipleRegressionInfo> listMRInfoBag = new ConcurrentBag<MultipleRegressionInfo>(listMRInfo);
                ConcurrentBag<double> vectorArrayBag = new ConcurrentBag<double>(output);
                ConcurrentBag<double[]> matrixList = new ConcurrentBag<double[]>();

                lock (_lock)
                {
                    matrixList.Add(input1.Where((v, k) => k != i).ToArray());
                    matrixList.Add(input2.Where((v, k) => k != i).ToArray());
                }

                var matrixArray2 = CreateMatrix.DenseOfColumnArrays(matrixList);
                var actualResult = vectorArrayBag.ElementAt(i);
                var newVectorArray = CreateVector.Dense(vectorArrayBag.Where((v, j) => j != i).ToArray());
                var items = FindBestMRSolution(matrixArray2, newVectorArray);
                double estimate1 = 0;

                if (items != null)
                {
                    lock (_lock)
                    {
                        var y = 0d;
                        var independentCount = matrixArray2.RowCount;
                        var dependentCount = newVectorArray.Count;

                        if (independentCount == dependentCount)
                        {
                            var populationCount = independentCount;
                            y = newVectorArray.Average();

                            for (int l = 0; l < matrixArray2.ColumnCount; l++)
                            {
                                var avg = matrixArray2.Column(l).Average();
                                y -= avg * items[l];
                            }
                        }

                        for (int m = 0; m < 2; m++)
                        {
                            var coefficient = items[m];

                            if (m == 0)
                            {
                                estimate1 += input1.ElementAt(i) * coefficient;
                            }
                            else
                            {
                                estimate1 += input2.ElementAt(i) * coefficient;
                            }
                        }

                        estimate1 += y;
                    }
                }
                else
                {
                    lock (_lock)
                    {
                        estimate1 = 0;
                    }
                }

                lock (_lock)
                {
                    press1 += Math.Pow(actualResult - estimate1, 2);
                }
            });

            for (int i = 0; i < output.Count; i++)
            {
                List<double[]> matrixList = new List<double[]>();
                matrixList.Add(input1.Where((v, k) => k != i).ToArray());
                matrixList.Add(input2.Where((v, k) => k != i).ToArray());
                var matrixArray = CreateMatrix.DenseOfColumnArrays(matrixList);
                var actualResult = output.ElementAt(i);
                var newVectorArray = CreateVector.Dense(output.Where((v, j) => j != i).ToArray());
                var items = FindBestMRSolution(matrixArray, newVectorArray);
                double estimate = 0;

                if (items != null)
                {
                    var y = CalculateYIntercept(matrixArray, newVectorArray, items);
                    for (int m = 0; m < 2; m++)
                    {
                        var coefficient = items[m];

                        if (m == 0)
                        {
                            estimate += input1.ElementAt(i) * coefficient;
                        }
                        else
                        {
                            estimate += input2.ElementAt(i) * coefficient;
                        }
                    }
                }
                else
                {
                    estimate = 0;
                }

                press2 += Math.Pow(actualResult - estimate, 2);
            }

            tss = CalculateTotalSumOfSquares(vectorArray.ToList());
            var test1 = 1 - (press1 / tss);
            var test2 = 1 - (press2 / tss);
}

public Vector<double> CalculateWithQR(Matrix<double> x, Vector<double> y)
    {
        Vector<double> result = null;

            result = MultipleRegression.QR(x, y);

            for (int i = 0; i < result.Count; i++)
            {
                var value = result.ElementAt(i);

                if (Double.IsNaN(value) || Double.IsInfinity(value))
                {
                    return null;
                }
            }

        return result;
    }

    public Vector<double> CalculateWithNormal(Matrix<double> x, Vector<double> y)
    {
        Vector<double> result = null;

            result = MultipleRegression.NormalEquations(x, y);

            for (int i = 0; i < result.Count; i++)
            {
                var value = result.ElementAt(i);

                if (Double.IsNaN(value) || Double.IsInfinity(value))
                {
                    return null;
                }
            }

        return result;
    }

    public Vector<double> CalculateWithSVD(Matrix<double> x, Vector<double> y)
    {
        Vector<double> result = null;

            result = MultipleRegression.Svd(x, y);

            for (int i = 0; i < result.Count; i++)
            {
                var value = result.ElementAt(i);

                if (Double.IsNaN(value) || Double.IsInfinity(value))
                {
                    return null;
                }
            }

        return result;
    }

    public Vector<double> FindBestMRSolution(Matrix<double> x, Vector<double> y)
    {
        Vector<double> result = null;

            result = CalculateWithNormal(x, y);

            if (result != null)
            {
                return result;
            }
            else
            {
                result = CalculateWithSVD(x, y);

                if (result != null)
                {
                    return result;
                }
                else
                {
                    result = CalculateWithQR(x, y);

                    if (result != null)
                    {
                        return result;
                    }
                }
            }

        return result;
    }

public double CalculateTotalSumOfSquares(List<double> dependentVariables)
    {
        double tts = 0;

            for (int i = 0; i < dependentVariables.Count; i++)
            {
                tts += Math.Pow(dependentVariables.ElementAt(i) - dependentVariables.Average(), 2);
            }

        return tts;
    }

实际产出(最新结果):

代码语言:javascript
复制
test1 = 137431.12889999992 (parallel for loop)

test2 = 7.3770258447689254E- (regular for loop)

结语:如何设置符合MCVE的测试。

这可能是一个公平的方法,准备一个确实完全可复制的DataSET-s,代码+A/B/C/.MCVE的设置放在一个准备运行的IDE和测试沙箱,超链接在这里,这样社区成员可以点击一个重新运行按钮,专注于根源分析,而不是解码和重新设计堆的不完整的SLOC。

如果这是针对O/P的,它将为其他社区成员运行,而O/P已经向他们寻求了答案或帮助。

在网上试试!

我的新版本代码:

代码语言:javascript
复制
public double CalculatePredictedRSquared()
    {
        Vector<double> output = CreateVector.Dense(Enumerable.Range(0, 400).Select(i => Convert.ToDouble(i)).ToArray());
        List<double> input1 = new List<double>(Enumerable.Range(0, 400).Select(i => Convert.ToDouble(i)));
        List<double> input2 = new List<double>(Enumerable.Range(200, 400).Select(i => Convert.ToDouble(i)));
        double tss = CalculateTotalSumOfSquares(output.ToList());

        IEnumerable<int> range = Enumerable.Range(0, output.Count);
        var query = range.Select(i => DoIt(i, output, input1, input2));
        var result = 1 - (query.Sum() / tss);
        return result;
    }

    public double DoIt(int i, Vector<double> output, List<double> input1, List<double> input2)
    {
        List<double[]> matrixList = new List<double[]>
                {
                    input1.Where((v, k) => k != i).ToArray(),
                    input2.Where((v, k) => k != i).ToArray()
                };
        var matrixArray = CreateMatrix.DenseOfColumnArrays(matrixList);
        var actualResult = output.ElementAt(i);
        var newVectorArray = CreateVector.Dense(output.Where((v, j) => j != i).ToArray());
        var items = FindBestMRSolution(matrixArray, newVectorArray);
        double estimate = 0;

        if (items != null)
        {
            var y = CalculateYIntercept(matrixArray, newVectorArray, items);
            for (int m = 0; m < 2; m++)
            {
                var coefficient = items[m];

                if (m == 0)
                {
                    estimate += input1.ElementAt(i) * coefficient;
                }
                else
                {
                    estimate += input2.ElementAt(i) * coefficient;
                }
            }
        }
        else
        {
            estimate = 0;
        }

       return Math.Pow(actualResult - estimate, 2);
    }
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-11-14 22:36:51

这整件事是狗的早餐,你应该完全放弃这种平行的做法。

重新开始。我想让你这么做。我希望您编写一个方法DoIt,该方法返回double,并接受一个int、i以及执行循环的单个迭代所需的任何其他状态。

然后,您将重写您的方法如下:

代码语言:javascript
复制
public double CalculatePredictedRSquared()
{
    Vector<double> output = whatever;
    // Whatever other state you need here
    IEnumerable<int> range = Enumerable.Range(0, output.Count);
    var query = range.Select(i => DoIt(i, whatever_other_state));
    return query.Sum();
}

明白了吗?DoIt是你现在循环中的东西。它必须接受i,和output,以及你需要传递给它的任何其他向量。它只能计算一倍--在这种情况下,估计误差的平方--并返回这个双倍。

它必须是纯的:它不能读取或写入任何非局部变量,它不能调用任何非纯方法,而且每次给定相同的输入时,它必须给出完全相同的结果。纯方法是最容易编写、读取、理解、测试和并行化的方法;在进行数学计算时,始终尝试编写纯方法。

DoIt**编写测试用例并对其进行测试**。这是一种纯方法;您应该能够编写大量的测试用例。类似地,测试DoIt调用的任何纯方法。

一旦您满意DoIt是正确的和纯的,那么魔术就会发生。只需将其改为:

代码语言:javascript
复制
range.AsParallel().Select...

然后比较并行版本和非并行版本。他们应该产生同样的结果;如果没有,那么有些事情是不洁的。弄清楚那是什么。

然后,验证并行版本是否更快。如果没有,那么您在DoIt中没有完成足够的工作来证明并行性是正确的;有关详细信息,请参阅

票数 12
EN

Stack Overflow用户

发布于 2017-11-16 16:21:23

有几件事:

代码语言:javascript
复制
lock (_lock)
{
   matrixList.Add(input1.Where((v, k) => k != i).ToArray());
   matrixList.Add(input2.Where((v, k) => k != i).ToArray());
}

您正在将项添加到已设计为线程安全的集合中,因此不需要锁定。虽然List不是线程安全的,但是并发读取应该是可以的。来自文档

在一个列表上执行多个读取操作是安全的,但是如果在读取集合时修改了集合,则可能会出现问题。若要确保线程安全,请在读写操作期间锁定集合。若要使多个线程能够访问集合以进行读写,您必须实现自己的同步。

还请注意,matrixList存储在局部变量中;在本例中,不能从多个线程调用集合,因为委托的整个主体都保证在同一个线程上运行--例如,Parallel.For循环主体的一半将在线程A上运行,另一半将在线程B上运行。

类似地,在对estimate1进行更改时没有理由锁定它,因为它不可能从其他线程修改。

免责声明:不能保证整个Parallel.For循环的并行度。甚至没有任何保证它将并行运行。

然而,press1press2不是局部变量,因此您确实需要以某种方式同步这些变量。(如果您找到了避免每次锁定的方法,那就更好了,因为这至少会部分地消除多线程的作用)。

也许最关键的是,ConcurrentBag是一个无序的集合。你没有显示你对矩阵所做的所有运算,但是如果你在任何地方做矩阵乘法,这很容易导致错误的结果。不能保证矩阵乘法会使通勤。而对于整数来说,A * B = B * A则不适用于矩阵。您的逻辑很可能微妙地依赖于以特定顺序发生的操作(因为ConcurrentBag是无序的)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47253645

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档