【问题标题】:Parallel For c#C#的并行
【发布时间】:2018-01-25 11:48:59
【问题描述】:

我正在尝试提高计算图像均值的方法的性能。

为此,我使用两个 For 语句来迭代所有图像,因此我尝试使用 Parallel For 来改进这一点,但结果不一样。

我做错了吗?或者是什么导致了差异?

public static double MeanDN(this GrayImage image)
{
  double mean = 0;
  int totalPixels = image.Width * image.Height;

  for (int i = 0; i < image.Height; i++)
      for (int j = 0; j < image.Width; j++)
          mean += (double)image[i, j] / totalPixels;

  double parallelMean = 0;

  Parallel.For(0, image.Height, i =>
  {
      for (int j = 0; j < image.Width; j++)
          parallelMean += (double)image[i, j] / totalPixels;
  });

  return mean;
}

输出:

平均值 = 404.12

parallelMean = 148.8658

【问题讨论】:

  • 同步? (还有 3 个)
  • Parallel.for 是异步方法。 for是同步方法。
  • 如何提高这种方法的性能?可以用Parallel for吗?
  • 正确答案是 404.12 吗?
  • 问题是你没有同步访问parallelMean。您还可以通过用局部变量替换 get on 属性来提高性能(var imageWidth = image.Width; for (x=0;xinstil.co/2016/03/21/parallelism-on-a-single-core-simd-with-c) 和其他类似指令集。

标签: c# parallel.foreach


【解决方案1】:

您正在访问一个资源(变量parallelMean)而没有从多个线程同步。有些更改会相互覆盖,有些读取会读取错误或旧数据。这就是您看到错误结果的原因。

最简单的解决方案是在对该变量的写访问权限周围放置一个lock 语句,但这会迫使它重新按顺序执行。

如果您需要对结果进行计算,使用 PLinq 可能更容易,它会为您完成大部分同步:

var mean = Enumerable.Range(0, image.Height)
                     .AsParallel()
                     .Select(i => Enumerable.Range(0, image.Width)
                                            .AsParallel()
                                            .Sum(j => (double)image[i, j] / totalPixels))
                     .Sum();

【讨论】:

  • 我明白你的意思,这提供了正确的结果。我现在将测量性能,谢谢
  • 请注意,AsParallel 也会增加一些开销,总体而言,仅在高度上执行此操作可能更快。
  • 用秒表比较时间后,我证实@Andrew 是对的。该解决方案比 Hakam Fostok 算法慢。
  • 并行性始终是一种权衡。你需要足够多的去做来让分散工作变得值得。图片越小,与实际工作相比开销就越大。
【解决方案2】:

您的问题是您让多个任务修改同一个变量parallelMean。所以你有一个种族条件
我也认为第二个结果不仅是假的,它也不稳定:意味着尝试再次执行代码,第二个结果会改变
我认为这段代码可以解决你的问题

double[] parallelMean = new double[image.Height];

Parallel.For(0, image.Height, i =>
{
    for (int j = 0; j < image.Width; j++)
        parallelMean[i] += (double)image[i, j] / totalPixels;
});

return parallelMean.Sum();

【讨论】:

  • 方法'For'没有重载需要3个参数
  • 很抱歉我修改了代码,我认为它现在应该可以工作了。
  • 我的解决方案现在与您的解决方案相似,只是我在“return parallelMean.Sum() / totalPixels”的末尾进行了除法。这使得迭代只做一次操作,除法只做一次,最后也不需要强制转换。考虑到几个测试,你的和这个修改的性能相似,但我最后选择了除法。
猜你喜欢
  • 2010-11-05
  • 1970-01-01
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 1970-01-01
  • 2019-04-02
  • 2011-07-10
  • 2010-10-06
相关资源
最近更新 更多