GDI+ 位图读写速度测试

2023-12-27 20:00:41

? Conmajia 2012-2023
Init. on 25 Aug. 2012
SN 128.2

本文仅测试 GDI+ 下 Bitmap 像素读写操作。若需极致的速度提升,建议参考 MMX/SSE 指令调用或利用 CUDA 等技术增加参与处理的核心数量。

使用 Bitmap.GetPixelBitmap.SetPixel 方法读写位图会消耗大量时间。因此,一般会将位图操作聚在一处然后对其内存锁定后再行读取,以加快操作速度——减少了安全检查的消耗。较为典型的例子有 MSDN 给出的如下代码。

private void LockUnlockBitsExample(PaintEventArgs e) {
	// 读入位图
	Bitmap bmp = new Bitmap("<img_path>");
	// 锁定
	Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
	System.Drawing.Imaging.BitmapData bmpData =
	    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
	    bmp.PixelFormat);
	// 首行指针
	IntPtr ptr = bmpData.Scan0;
	// 新建位图像素数组
	int bytes  = Math.Abs(bmpData.Stride) * bmp.Height;
	byte[] rgbValues = new byte[bytes];
	// 复制像素 RGB 值
	System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
    // 将所有像素 R 值设为 255(最大),图会变红
	for (int counter = 2; counter < rgbValues.Length; counter += 3)
	    rgbValues[counter] = 255;
	// 将修改后的像素数组写回位图
	System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
	// 解锁
	bmp.UnlockBits(bmpData);
	// 绘出位图
	e.Graphics.DrawImage(bmp, 0, 150);
}

这一示例按以下流程完成对位图的修改。

🔒
🔓
🕹?图像处理
🖥 复制到内存
🖼?写位图
🖼?读位图
🤡其他

对比未锁定内存的普通处理方式,图 1 展示了这个比较的过程。

图 1. 测试程序截图

为减少变量影响,所有测试均在单颗 CPU 核心上运行(图 2)。

图 2. 单核运行测试程序

测试用例均为 1:1 长宽比的 24bpp 位图,从 64 x 64 像素(13 KB)到 8192 x 8192 像素(196 MB)列于图 3。

图 3. 测试用例

得到列于图 4 的测试结果。

图 4. 测试结果

可见,平均性能提升约 800% 至 2300%,得到极大提升。

测试程序下载:点击下载

后记

有杠精朋友指出:

这种 LockBits 是不适合……要玩速度……考虑用汇编进一步优化……比如,最简单的反色算法,3000400024 的图像,一般的语言要100 ms 左右的处理时间,用汇编的话 20 ms 够了……

废不废话先不谈,这位朋友似乎对理解我写的文字这件事上有点困难,得出了我在追求绝对速度的结论。无所谓,那么我就来试试不用汇编,反色能做到多快。

测试用例选为 #7 4096 x 4096,大于汇编爱好者举例的 3000 x 4000 @ 24bpp 图像。大了一些,但是不多。

测试用平台:Intel i3 380M (2.53 GHz)/4 GB DDR3-1333/Win7 32bit

采取临时锁内存方式处理反色操作,代码无优化,用时约 300 ms,如图 5。

图 5. 第一次位图反色用时测试(4096 x 4096 @ 24bpp)

简单优化变量使用和循环效率,第二次测试用时约 250 ms,速度提升了约 50 ms (17%),如图 6。

图 6. 第二次位图反色用时测试(4096 x 4096 @ 24bpp)

这个结果比起“一般的语言 100 ms 左右”还相差甚远。引入并行计算后,测试用时低于 50 ms,效率较无优化测试提升 500%,较为接近“用汇编的话 20 ms 够了”的程度(图 7)。

图 7. 第三次位图反色用时测试(4096 x 4096 @ 24bpp)

当然,这个结果和测试平台强关联,换一颗强劲的 CPU 以上的测试都将成为笑话🤡。

网友参与

在这位朋友跟我抬杠的过程中,网友们纷纷下场凑热闹,给出一些他们的测试方法和结果。现将投稿中较高速度的结果列举如下。

本文测试

实现:原生 C#
配置:本文平台
用时:46 ms

测试 1

投稿:laviewpbt(ImageWizard 开发者)
实现:汇编/VB.NET
配置:同本文平台
用时:25 ms

测试 2

投稿:兰征鹏(C/C++ 领域技术专家,创意鹰翔技术大拿)
实现:VC++.NET 调用 SSE 指令
配置:i7 860 (2.93 GHz)/12 GB DDR3-1333/Win7 64bit
用时:12 ms 至 19 ms
注:该测试平台性能约为本文平台 3 倍,未考虑内存影响。参见 UserBenchmark

测试 3

投稿:xiaotie(Geb.Image 开发者)
实现:自定义图像库/C# 指针
配置:优于 测试 1
用时:33 ms

大家玩得还挺开心的。

? Conmajia 2012

文章来源:https://blog.csdn.net/conmajia/article/details/135240904
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。