![](/img/trans.png)
[英]Why my code does not speed up with a multithreaded Parallel.For loop?
[英]Parallel class does not provide any speed up
我正在尝试创建一种方法,该方法将过滤掉低于给定灰度阈值的所有像素(例如,下面的全部为黑色,上面的全部为白色)。 该方法有效,但是没有我想象的那么快。
我决定使用Parallel
类,但是无论我设置MaxDegreeOfParallelism
我都不会获得任何速度优势。 我也对位图执行其他一些操作,无论MaxDegreeOfParallelism
是什么,操作的总时间始终约为170 ms。 调试时,执行此过滤本身所需的时间约为160毫秒,因此我认为总体差异会很大。
我正在使用i7处理器,4个物理核心,8个逻辑核心。
编码:
Color black = System.Drawing.Color.FromArgb(0, 0, 0);
Color white = System.Drawing.Color.FromArgb(255, 255, 255);
int lowerBound = (int)((float)lowerBoundPercent * 255.0 / 100.0);
int upperBound = (int)((float)upperBoundPercent * 255.0 / 100.0);
int[][] border = new int[8][];
for (int i=0;i<8;i++)
{
border[i] = new int[] { i*height/8, (i+1)*height/8-1};
}
Parallel.For(0, 8, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
for (int k = 0; k < width; k++)
{
for (int j = border[i][0]; j <= border[i][1]; j++)
{
Color pixelColor;
int grayscaleValue;
pixelColor = color[k][j];
grayscaleValue = (pixelColor.R + pixelColor.G + pixelColor.B) / 3;
if (grayscaleValue >= lowerBound && grayscaleValue <= upperBound)
color[k][j] = white;
else
color[k][j] = black;
}
}
});
color[][]
是System.Drawing.Color
的锯齿状数组。
问题:这正常吗? 如果没有,我该怎么做才能改变它?
编辑:
像素提取:
Color[][] color;
color = new Color[bitmap.Width][];
for (int i = 0; i < bitmap.Width; i++)
{
color[i] = new Color[bitmap.Height];
for (int j = 0; j < bitmap.Height; j++)
{
color[i][j] = bitmap.GetOriginalPixel(i, j);
}
}
位图是我自己的类位图的一个实例:
public class Bitmap
{
System.Drawing.Bitmap processed;
//...
public Color GetOriginalPixel(int x, int y) { return processed.GetPixel(x, y); }
//...
}
not just DegreeOfParallelism . 要回答关于为什么并行方法没有更快的主要问题, Parralel.For
仅从一个线程开始,然后添加更多主题,因为它检测到更多线程可能在加快工作速度方面是有益的,请注意并行选项是不仅是DegreeOfParallelism 。 很简单,循环的迭代次数不足以使足够多的线程生效,因此您需要减少每次迭代的工作量。
尝试通过宽度的循环而不是高度的8个块来给并行操作做更多的工作。
Color black = System.Drawing.Color.FromArgb(0, 0, 0);
Color white = System.Drawing.Color.FromArgb(255, 255, 255);
int lowerBound = (int)((float)lowerBoundPercent * 255.0 / 100.0) * 3;
int upperBound = (int)((float)upperBoundPercent * 255.0 / 100.0) * 3;
Parallel.For(0, width, k =>
{
for (int j = 0; j < height; j++)
{
Color pixelColor;
int grayscaleValue;
pixelColor = color[k][j];
grayscaleValue = (pixelColor.R + pixelColor.G + pixelColor.B);
if (grayscaleValue >= lowerBound && grayscaleValue <= upperBound)
color[k][j] = white;
else
color[k][j] = black;
}
});
我不会同时进行宽度和高度的选择,然后您可能会遇到一个相反的问题,即没有给每个迭代足够的工作。
我强烈建议您下载并阅读“并行编程模式” ,它在讨论您应该为Parallel.For
多少工作时使用了这个确切的示例。 从C#版本的第26页底部开始,查看“ 非常小的循环体 ”和“ 太细粒度,太粗粒度 ”的“反模式”,以查看遇到的确切问题。
我还将研究使用LockBits来读入和读出像素数据,而不是像评论中讨论的那样使用GetPixel和SetPixel。
通过使用LockBits
我设法将时间从每帧LockBits
ms减少到了LockBits
ms。 然后,我继续进行一些研究,并将LockBits
与不安全上下文中的指针操作和Parallel.For循环结合在一起。 结果代码:
位图类:
public class Bitmap
{
System.Drawing.Bitmap processed;
public System.Drawing.Bitmap Processed { get { return processed; } set { processed = value; } }
// ...
}
方法:
int lowerBound = 3*(int)((float)lowerBoundPercent * 255.0 / 100.0);
int upperBound = 3*(int)((float)upperBoundPercent * 255.0 / 100.0);
System.Drawing.Bitmap bp = bitmap.Processed;
int width = bitmap.Width;
int height = bitmap.Height;
Rectangle rect = new Rectangle(0, 0, width, height);
System.Drawing.Imaging.BitmapData bpData = bp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bp.PixelFormat);
unsafe
{
byte* s0 = (byte*)bpData.Scan0.ToPointer();
int stride = bpData.Stride;
Parallel.For(0, height, y1 =>
{
int posY = y1 * stride;
byte* cpp = s0 + posY;
for (int x =0; x<width; x++)
{
int total = cpp[0] + cpp[1] + cpp[2];
if (total >= lowerBound && total <= upperBound)
{
cpp[0] = 255;
cpp[1] = 255;
cpp[2] = 255;
cpp[3] = 255;
}
else
{
cpp[0] = 0;
cpp[1] = 0;
cpp[2] = 0;
cpp[3] = 255;
}
cpp += 4;
}
});
}
bp.UnlockBits(bpData);
通过Parallel.For
循环中的这种工作划分,代码将在1-5毫秒内执行,这意味着速度提高了约70倍!
我尝试将循环的块增大4倍和8倍,并且时间范围仍然是1-5毫秒,因此我不再赘述。 循环足够快。
非常感谢您的回答,斯科特,也感谢大家在评论中的投入。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.