为什么我的不安全代码阻塞比我的安全代码慢?

我正在尝试编写一些能够方便地处理video帧的代码。 我收到帧作为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 = 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 Back Buffer而不是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 = 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)); 

所以我正在分析相同的图像。 传入video帧流的测试运行:

 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 

在每次循环迭代。 将结果存储在变量中。

在安全或不安全的代码中进一步优化:在循环内停止除以3。 在循环外将阈值乘以3。 您将需要使用除byte之外的某些类型,但这应该不是问题。 实际上,你已经使用了比byte更大的数据类型:)

很难说没有对代码进行分析,特别是因为代码非常不同(虽然它起初看起来很相似)一些关键点(它们都只是推测)

如果在不安全版本中计算的if不在保险箱中,则停止条件

  • 数组pixelArray的索引只有在它们被使用两次时才会被计算出来。
  • 即使它们没有“缓存”,将数字加在一起而不存储它们(与++ p相反)仍然会更快(更少的指令和更少的内存访问)
  • 您没有在安全版本中锁定位图
  • pixelArray [i],pixelArray [i + 1],pixelArray [i + 2]可能会存储在本地,这使得第二次访问它们的速度可能比再次迭代指针要快得多。
  • 你在不安全的代码中有一个额外的赋值(pCOpy = pBackBuffer)和一个额外的增量(pBackBuffer ++;)

这就是我能提出的所有想法。 希望能帮助到你