利用内存拷贝函数提高C/C++程序运行速度的研究

2018-01-23 10:21张合花张全法李素晓
中州大学学报 2017年6期
关键词:拷贝备份灰度

张合花,张全法,李素晓

(郑州大学 物理工程学院,郑州 450001)

C语言程序设计和C++语言程序设计是许多高校开设的计算机语言课程,C/C++也是广大工程技术人员最常用的程序设计语言之一[1]。C/C++程序可以获得很高的运行速度,但是并非所有的C/C++程序一经写出就具有了无可超越的运行速度,往往还需要进行优化。优化一般包括提高运行速度和缩小可执行文件大小两方面的含义,本文提及的优化是指前者。

优化可以分为自动优化和人工优化两种方式。自动优化是指利用C/C++编译器的优化选项进行优化。一般的C/C++编译器支持静态自动优化,采用LLVM(Low Level Virtual Machine)编译架构时还可以支持动态自动优化[2]。人工优化是指程序员利用经验和技巧进行优化,常用方法包括采用自增自减运算、利用指针处理数组、合理使用内联函数、采用位运算、合理使用寄存器变量以及尽量将浮点数运算转化为整数运算等[3-7]。本文主要讨论如何利用内存拷贝函数对C/C++程序进行人工优化。

1 内存拷贝函数的使用方法和条件

内存拷贝函数memcpy()是C运行库中的函数,在C++中仍然可以使用,使用时一般需要包含头文件memory.h。在VC 6.0中其函数原型为

void * memcpy(void * dest,const void * src,

size_t count),

其功能是将以src为数据源首地址的内存中的数据拷贝到以dest为目的地首地址的内存中,要拷贝的数据量为count字节。由于采用了快速内存拷贝技术,其运行速度比循环赋值快得多(参见后面的实验结果)。因此,在需要拷贝大量数据时,利用它可以显著提高程序的运行速度。

在VC 6.0的帮助系统MSDN中仅要求这两块内存不相互重叠(否则不能正确地拷贝所有数据),对于3个参数并没有特殊要求和说明。但是,实践发现只有当src和dest皆为字的首地址且count为字长的整数倍时,才可以获得最快的运行速度。实际应用中可能不满足这些条件,需要采取适当的措施尽量满足这些条件。

2 利用内存拷贝函数提高运行速度

以利用VC 6.0编写实时图像处理中经常用到的灰度图像空间域均值滤波函数为例,说明如何采取措施提高程序的运行速度。

2.1 空间域均值滤波算法简介

假设滤波前的灰度图像用g表示,宽M像素,高N像素;滤波后的图像用b表示;所用模板宽(2K+1)像素,高(2L+1)像素。M、N、K、L皆为整数。空间域均值滤波算法可以描述为

(1)

式中:bij—图像b上坐标为(i,j)的像素的灰度值;

gmn—图像g上坐标为(m,n)的像素的灰度值。

对于图像中部的像素,根据式(1)求bij不存在任何问题。对于图像边缘附近的像素,根据式(1)求bij将出现困难,因为此时与模板对应的一些像素不存在。处理此问题的方法有多种,包括对边缘或角上的像素设计特殊模板、假设图像是环绕的、不处理边缘像素等[8]。

2.2 空间域均值滤波的快速实现方法简介

由于涉及大量的运算,在那些还需要实时进行其他复杂处理的应用中,必须采取措施来提高其运行速度。最简单的方法是采用K=1,L=1的最小模板来减少计算量。从另一角度来看,由于模板越大图像信息丢失越严重,K和L也不宜太大。此外,不处理边缘像素也有助于减少计算量。

文献[9]介绍的盒滤波方法利用上、下或左、右两个模板位置之间的相关性来减少重复计算,从而提高其运行速度。它利用一个含M个元素的数组B来存储图像g上以第j行为中心的(2L+1)行相应列像素灰度值之和。计算图像b的第一行时通过求和初始化B,计算完一行之后通过加上新的一行、减去旧的一行更新B。求bij时先计算B中以第i个元素为中心的(2K+1)个元素的和S,再将S除以(2K+1)与(2L+1)的乘积。每一行的第一个S通过求和初始化,模板向右移动时,S通过加上新的元素、减去旧的元素来更新。

对于相同大小的模板,从减少重复计算的角度看,盒滤波已经做到了极限[6]。文献[6]通过实验证明其运行速度可以进一步提高,采取的措施包括:通过指针访问内存中的数据,并利用自增运算移动指针;在K=1、L=1时利用3个临时变量取代数组B来保存中间结果,从而将内存访问转化为寄存器访问;将除法运算转化为移位运算;等等。

2.3 空间域均值滤波的数据拷贝分析

如果不需要将滤波后的图像写入原始图像的数据存储区,文献[6]所获得的速度已经没有多大提升空间(除非使用更快的硬件),反之,还可以显著提高,这需要分析此种情况下的数据拷贝操作。为了方便,假设不处理边缘像素,并且计算机的内存足够用。不处理边缘像素并不意味着丢弃边缘像素(例如赋零),而是保留边缘像素原来的灰度值,因为丢弃边缘像素对图像的影响比较大。内存不够用时编程将很复杂,但这不属于本文讨论的范畴。

根据式(1)可知,求bij时所用的gmn是原始图像上像素的灰度值,如果将求出的bij直接写入原始图像数据存储区而不采取措施,将严重影响后面的计算。处理这个问题的方法可以分为有备份法和无备份法两种。有备份法需要预先将原始图像的数据备份到临时存储区,然后根据临时存储区中的数据计算bij并直接写入原始图像数据存储区。无备份法不需要备份原始图像数据,它是根据原始图像数据计算bij并写入临时存储区,等到计算完成后再将临时存储区中的数据写入原始图像数据存储区,不过此时需要分行拷贝,并拷贝到正确位置。

通过以上分析可知,如果需要将滤波后的图像写入原始图像的数据存储区,无论有备份法还是无备份法皆不可避免大量的数据拷贝操作(数据备份实质上也是数据拷贝)。如果能够实现数据的快速拷贝则可以进一步提高其运行速度。

2.4 利用内存拷贝函数提高运行速度的分析

一般认为,减少要拷贝的数据量可以提高数据拷贝的速度。在不处理边缘像素时,有备份法要拷贝的数据量为M×N字节(灰度数据占1个字节),无备份法为(M-2K)×(N-2L)字节。这是因为无备份法不需要拷贝边缘像素的灰度值,而有备份法则很难避免备份边缘像素的灰度值,如果采取复杂措施刻意避免,往往得不偿失。因此,无备份法要拷贝的数据量比较少,有可能获得较快的速度。

利用memcpy()函数提高数据的拷贝速度也有助于提高运行速度。有些人不知道C语言的运行库中有专门用于拷贝内存数据的函数,即使知道该函数,也有一部分人认为使用它未必就好,因为一般常识是函数调用需要额外的开销,通常会影响运行速度。另外,通过循环赋值实现数据拷贝也很容易。因此,很多人采取循环赋值方式实现数据拷贝。但是,当数据拷贝操作在所有操作中所占比例较大时,memcpy()函数的优势就显现出来了。

通过以上分析似乎可以得出memcpy()函数与无备份法相结合是处理数据拷贝问题的最佳组合的结论,其实不然。因为要充分发挥该函数的优势还需要满足一些条件,而采用无备份法又不处理边缘像素时无法同时满足这些条件。这是因为:每次要拷贝的数据量(M-2K)通常不是字长的整数倍;尽管系统分配的数据存储区的首地址是字的首地址,由于(M-2K)通常不是字长的整数倍,使得src通常不是字的首地址;为了拷贝到正确位置,dest通常也不是字的首地址;多次拷贝会影响速度。

采用有备份法可以同时满足这些条件。由系统分配的数据存储区的首地址是字的首地址,从而保证了src和dest皆是字的首地址;由摄像器件产生的图像的宽度M是字长的整数倍,从而保证了要拷贝的数据量是字长的整数倍(对于人工随意裁剪的图像,存储时每一行的数据量必须是字长的整数倍才能够正确显示)。因此,memcpy()函数与有备份法相结合反而有可能获得更快的速度,尽管不处理边缘像素时需要拷贝的数据总量比较多。实验证明确实如此。

3 实验设计与结果

3.1 实验条件和手段

所用计算机的CPU为Intel Pentium Dual E2140处理器,主频为1.60GHz,程序用VC 6.0编写。在发行版下利用VC 6.0的Build/Profile功能测试函数的运行时间,自动优化选项为最大速度。用一段彩色视频作为处理对象,该段视频共3845帧,每帧图像的M=384,N=288。

对于每帧图像,首先采用文献[5]中的方法进行灰度化,然后调用空间域均值滤波函数对灰度图像进行滤波。设计空间域均值滤波函数时所用模板的K=1,L=1,并且不处理边缘像素。为了进行比较,采用不同方法进行设计,并分别进行测试。为了减少Windows多任务特性的影响,每种设计都需要完成5次整段视频的处理,每完成一次处理记录一次空间域均值滤波函数的总运行时间,最后计算平均总运行时间。

3.2 空间域均值滤波函数的不同设计

设计空间域均值滤波函数时涉及数据计算与数据拷贝两个部分。数据计算部分的功能是求bij,采用文献[6]中的方法进行快速计算。区别在于:若采用无备份法,gmn来源于原始图像数据存储区,bij写入临时存储区;若采用有备份法,gmn来源于临时存储区,bij直接写入原始图像数据存储区。数据拷贝部分采用不同的实现方法,具体如下:

设计1 采用无备份法,数据拷贝通过分行循环赋值(故意这样)实现。

设计2 采用无备份法,数据拷贝通过多次(必须这样)调用memcpy()函数实现。

设计3 采用有备份法,数据拷贝通过多次(故意这样)调用memcpy()函数实现。

设计4 采用有备份法,数据拷贝通过一次(可以这样)调用memcpy()函数实现。

为了不失公允,所有设计都尽可能多地采取措施提高运行速度。所采取的措施包括赋值缩写、指针访问、指针自增运算和指针比较等。指针比较是指通过比较指针变量是否到达结束位置作为是否结束循环的条件,从而避免使用额外的循环变量。

3.3 实验结果与分析

实验结果如表1所示。根据设计2和设计1的平均总运行时间可知,利用memcpy()函数拷贝数据比通过循环赋值快得多。根据设计3和设计2的平均总运行时间可知,当满足所需条件时可以充分发挥memcpy()函数的优势,尽管设计3比设计2每行需要多拷贝2个字节,并且每帧图像需要多拷贝2行。根据设计4和设计3的平均总运行时间可知,整体拷贝比分行拷贝快。可以求出,设计4处理一帧图像所用的平均时间为0.668 ms,运行速度比设计1提高了8.3%。

表1 不同设计的总运行时间 ms

根据上述实验数据,设计4比设计1的运行速度提高得似乎很有限,但是上述总运行时间中还包含了数据计算部分所需要的时间,如果将数据计算部分注释掉,再按照上述方法进行测试,求得设计1~设计4数据拷贝部分的平均总运行时间分别为375ms,224ms,188ms和142 ms,设计2~设计4比设计1的数据拷贝部分运行速度分别提高了40 %,50 %和62 %。这充分说明了使用memcpy()函数取代循环赋值的必要性,也说明了正确使用该函数的重要性。

4 结语

本文讨论了利用memcpy()函数对C/C++程序进行人工优化的问题。以设计图像处理中常用的空间域均值滤波函数为例,讨论了如何通过分析寻找进一步提高程序运行速度的途径,并通过实验证明利用本文所述方法可以显著提高程序的运行速度。这种优化程序的思想方法可以为工程技术人员和有关人员设计需要快速处理大量数据的C/C++程序提供参考。

[1]张合花,张全法,齐永奇.准确使用C/C++中等于运算符的研究[J].中州大学学报,2016,33(2):112-115.

[2]朱晓珺,李冬梅.C/C++程序的运行时优化研究[J].软件导刊,2009,8(4):60-62.

[3]唐娟.合理提高C/C++程序效率的方法[J].孝感学院学报,2006,26(3):68-70.

[4]张志军,孙志辉.基于VC平台的彩色图像的灰度化技术[J].自动化技术与应用,2005,24(1):61-63.

[5]张全法,杨海彬,任朝栋,等.彩色图像的快速高保真灰度化方法研究[J].郑州大学学报(理学版),2011,43(3):66-69.

[6]张全法,费光彦,王庆峰,等.视频车辆监控系统图像滤波算法的研究[J].应用光学,2012,33(1):85-89.

[7]张全法,陈倩,王东亚.帧差分智能视频监控系统图像亮度的校正[J].应用光学,2013,34(5):778-783.

[8]Russ J C.数字图像处理[M].6版.余翔宇,等,译.北京:电子工业出版社,2014:135-140.

[9]舒志龙,阮秋琦.一种二维均值滤波快速算法及其应用[J].北方交通大学学报,2001,25(2):22-24.

猜你喜欢
拷贝备份灰度
浅谈数字化条件下的数据备份管理
采用改进导重法的拓扑结构灰度单元过滤技术
Bp-MRI灰度直方图在鉴别移行带前列腺癌与良性前列腺增生中的应用价值
Arduino小车巡线程序的灰度阈值优化方案
创建vSphere 备份任务
Windows10应用信息备份与恢复
文化拷贝应该如何“拷”
文化拷贝应该如何“拷”
旧瓶装新酒天宫二号从备份变实验室
食管腺癌DNA拷贝变化相关基因的生物信息学分析