为什么我的不安全代码块比我的安全代码慢?
我正在尝试编写一些代码,可以方便地处理视频帧。我正在接收帧为System.Windows.Media.Imaging.WriteableBitmap
。出于测试目的,我只是应用一个简单的阈值过滤器来处理BGRA格式图像,并根据BGR像素的平均值将每个像素分配为黑色或白色。为什么我的不安全代码块比我的安全代码慢?
这里是我的“安全”的版本:
public static void ApplyFilter(WriteableBitmap Bitmap, byte Threshold)
{
// Let's just make this work for this format
if (Bitmap.Format != PixelFormats.Bgr24
&& Bitmap.Format != PixelFormats.Bgr32)
{
return;
}
// Calculate the number of bytes per pixel (should be 4 for this format).
var bytesPerPixel = (Bitmap.Format.BitsPerPixel + 7)/8;
// Stride is bytes per pixel times the number of pixels.
// Stride is the byte width of a single rectangle row.
var stride = Bitmap.PixelWidth * bytesPerPixel;
// Create a byte array for a the entire size of bitmap.
var arraySize = stride * Bitmap.PixelHeight;
var pixelArray = new byte[arraySize];
// Copy all pixels into the array
Bitmap.CopyPixels(pixelArray, stride, 0);
// Loop through array and change pixels to black/white based on threshold
for (int i = 0; i < pixelArray.Length; i += bytesPerPixel)
{
// i=B, i+1=G, i+2=R, i+3=A
var brightness =
(byte)((pixelArray[i] + pixelArray[i+1] + pixelArray[i+2])/3);
var toColor = byte.MinValue; // Black
if (brightness >= Threshold)
{
toColor = byte.MaxValue; // White
}
pixelArray[i] = toColor;
pixelArray[i + 1] = toColor;
pixelArray[i + 2] = toColor;
}
Bitmap.WritePixels(
new Int32Rect(0, 0, Bitmap.PixelWidth, Bitmap.PixelHeight),
pixelArray, stride, 0
);
}
这是我认为是使用不安全的代码块和WriteableBitmap的后台缓冲区,而不是forebuffer直接翻译:
public static void ApplyFilterUnsafe(WriteableBitmap Bitmap, byte Threshold)
{
// Let's just make this work for this format
if (Bitmap.Format != PixelFormats.Bgr24
&& Bitmap.Format != PixelFormats.Bgr32)
{
return;
}
var bytesPerPixel = (Bitmap.Format.BitsPerPixel + 7)/8;
Bitmap.Lock();
unsafe
{
// Get a pointer to the back buffer.
byte* pBackBuffer = (byte*)Bitmap.BackBuffer;
for (int i = 0;
i < Bitmap.BackBufferStride*Bitmap.PixelHeight;
i+= bytesPerPixel)
{
var pCopy = pBackBuffer;
var brightness = (byte)((*pBackBuffer
+ *++pBackBuffer
+ *++pBackBuffer)/3);
pBackBuffer++;
var toColor =
brightness >= Threshold ? byte.MaxValue : byte.MinValue;
*pCopy = toColor;
*++pCopy = toColor;
*++pCopy = toColor;
}
}
// Bitmap.AddDirtyRect(
// new Int32Rect(0,0, Bitmap.PixelWidth, Bitmap.PixelHeight));
Bitmap.Unlock();
}
这是我第一次涉及不安全的代码块和指针,所以也许逻辑不是最优的。
我已经在使用相同的测试WriteableBitmaps代码两个块:
var threshold = Convert.ToByte(op.Result);
var copy2 = copyFrame.Clone();
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
BinaryFilter.ApplyFilterUnsafe(copyFrame, threshold);
stopWatch.Stop();
var unsafesecs = stopWatch.ElapsedMilliseconds;
stopWatch.Reset();
stopWatch.Start();
BinaryFilter.ApplyFilter(copy2, threshold);
stopWatch.Stop();
Debug.WriteLine(string.Format("Unsafe: {1}, Safe: {0}",
stopWatch.ElapsedMilliseconds, unsafesecs));
所以我分析同一图像。视频帧的输入流的试运行:
Unsafe: 110, Safe: 53
Unsafe: 136, Safe: 42
Unsafe: 106, Safe: 36
Unsafe: 95, Safe: 43
Unsafe: 98, Safe: 41
Unsafe: 88, Safe: 36
Unsafe: 129, Safe: 65
Unsafe: 100, Safe: 47
Unsafe: 112, Safe: 50
Unsafe: 91, Safe: 33
Unsafe: 118, Safe: 42
Unsafe: 103, Safe: 80
Unsafe: 104, Safe: 34
Unsafe: 101, Safe: 36
Unsafe: 154, Safe: 83
Unsafe: 134, Safe: 46
Unsafe: 113, Safe: 76
Unsafe: 117, Safe: 57
Unsafe: 90, Safe: 41
Unsafe: 156, Safe: 35
为什么我的不安全版本总是会慢?是否由于使用后台缓冲区?或者我做错了什么?
感谢
也许是因为你的不安全的版本是做乘法和属性访问:
Bitmap.BackBufferStride*Bitmap.PixelHeight
在每一个循环迭代。将结果存储在一个变量中。
不知道这是问题所在,但肯定会对它有所贡献。 – McAden 2010-05-03 19:27:40
你是对的!我非常想弄清楚不安全块如何工作和指针,我对我的迭代器条件逻辑操作一无所知。将它存储在一个变量中确实让我跌破了“安全”版本,尽管还不足以成为改变游戏规则的人。如果还有其他事情我做的是不是最优的,我全都是耳朵,如果不是的话,再次感谢您的快速解决! – 2010-05-03 19:30:25
这不是繁复的主要问题,而是你正在访问的属性(BackBufferStride和PixelHeight,这是真正的杀手锏。我知道你正在使用WPF,但是对于Silverlight,我创建了一个缓存的包装器所有的位图属性值,以获得所需的性能 – 2010-05-03 20:25:44
在安全或不安全代码中进一步优化: 在循环中停止除以3。在循环之外将您的阈值乘以3。您需要使用byte
以外的其他类型,但这不应该成为问题。实际上,你已经使用了比byte
更大的数据类型:)
+1:这也有帮助!我得到约26-30毫秒的范围之前和现在在循环之外执行单个乘法,正如你所建议的那样,将它缩小到17-20毫秒的范围内。不知道我能够降低得多。 – 2010-05-03 20:15:43
@jomtois我试过几件事...但是如果我的测试证明什么的话,微型优化通常是不值得的,我尝试使用一些位掩码并使用'while(pBackBuffer
很难说出代码分析,尤其是代码非常不同(尽管它看起来很相似),但一些关键点(它们是一切都只是猜测)
停止条件,如果如果不安全的版本是不计算在安全
- 的指数pixelArray 可能eventhough他们使用了两次只能使用一次 计算阵列。
- 即使他们没有“缓存”,并称 的数字加在一起而不存储 他们(而不是++ P)将仍然 更快(更少的指令和 更少的内存存取)
- 你是不是锁定位图在 安全版本
- pixelArray [I],pixelArray [I + 1],pixelArray [I + 2] 可能会存储在当地人制作 访问它们第二次 可能比一次迭代 指针快。
- 你在 不安全的代码(PCOPY = pBackBuffer)和 一个额外的增量额外分配(pBackBuffer ++;)
这就是所有的想法我可以拿出。希望它有帮助
我怀疑停止条件是如上所述的大打者。我会检查一下你的其他想法和@ Thorarin的想法。基本上我想要将指针移动到代表像素的4个字节的块中。我需要询问并更改块中的前三个字节并跳过第四个字节。我不知道微型优化是最好的。 – 2010-05-03 19:59:27
你是否在Release版本中运行这些测试? – 2010-05-03 19:21:43
现在不在调试器中。 – 2010-05-03 19:23:35
我建议在发布版本中运行性能测试以获得有意义的结果。如果你让优化器与代码一起使用,你可能会发现性能差距不足以证明使用'不安全'版本的理由。 – 2010-05-03 20:12:04