VC++多线程下内存操作的优化

上传人:z**** 文档编号:124743028 上传时间:2022-07-25 格式:DOC 页数:6 大小:44KB
返回 下载 相关 举报
VC++多线程下内存操作的优化_第1页
第1页 / 共6页
VC++多线程下内存操作的优化_第2页
第2页 / 共6页
VC++多线程下内存操作的优化_第3页
第3页 / 共6页
亲,该文档总共6页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
VC+多线程下内存操作的优化许多程序员发现用VC+编写的程序在多处理器的电脑上运行会变得很慢,这种情况多是 由于多个线程争用同一个资源引起的。对于用VC+编写的程序,问题出在VC+的内存管理 的具体实现上。以下通过对这个问题的解释,提供一个简便的解决方法,使得这种程序在多 处理器下避免出现运行瓶颈。这种方法在没有VC+程序的源代码时也能用。问题C和C+运行库提供了对于堆内存进行管理的函数:C提供的是malloc()和free()、C+ 提供的是new和delete。无论是通过malloc()还是new申请内存,这些函数都是在堆内存 中寻找一个未用的块,并且块的大小要大于所申请的大小。如果没有足够大的未用的内存块, 运行时间库就会向操作系统请求新的页。页是虚拟内存管理器进行操作的单位,在基于 Intel的处理器的NT平台下,一般是4,096字节。当你调用free()或delete释放内存时, 这些内存块就返还给堆,供以后申请内存时用。这些操作看起来不太起眼,但是问题的关键。问题就发生在当多个线程几乎同申请内 存时,这通常发生在多处理器的系统上。但即使在一个单处理器的系统上,如果线程在错误 的时间被调度,也可能发生这个问题。考虑处于同一进程中的两个线程,线程1在申请1,024字节的内存的同时,运行于另外一 个处理器的线程2申请256字节内存。内存管理器发现一个未用的内存块用于线程1,同时 同一个函数发现了同一块内存用于线程2。如果两个线程同时更新内部数据结构,记录所申 请的内存及其大小,堆内存就会产生冲突。即使申请内存的函数者成功返回,两个线程都确 信自己拥有那块内存,这个程序也会产生错误,这只是个时间问题。产生这种情况称为争用,是编写多线程程序的最大问题。解决这个问题的关键是要用一个 锁定机制来保护内存管理器的这些函数,锁定机制保证运行相同代码的多个线程互斥地进 行,如果一个线程正运行受保护的代码,则其他的线程都必须等待,这种解决方法也称作序 列化。NT提供了一些锁定机制的实现方法。CreateMutex()创建一个系统范围的锁定对象, 但这种方法的效率最低;nitializeCriticalSection()创建的critical section相对效率就要高许多; 要得到更好的性能,可以用具有service pack 3的NT 4的spin lock,更详细的信息可以参考 VC+帮助中的InitializeCriticalSectionAndSpinCount()函数的说明。有趣的是,虽然帮助文件 中说spin lock用于NT的堆管理器(HeapAlloc()系列的函数),VC+运行库的堆管理函数并 没有用spin lock来同步对堆的存取。如果查看VC+运行库的堆管理函数的源程序,会发现 是用一个critical section用于全部的内存操作。如果可以在VC+运行库中用HeapAlloc(), 而不是其自己的堆管理函数,将会因为使用的是spin lock而不是critical section而得到速度 优化。通过使用critical section同步对堆的存取,VC+运行库可以安全地让多个线程申请和释放 内存。然而,由于内存的争用,这种方法会引起性能的下降。如果一个线程存取另外一个线 程正在使用的堆时,前一个线程就需要等待,并丧失自己的时间片,切换到其他的线程。线 程的切换在NT下是相当费时的,因为其占用线程的时间片的一个小的百分比。如果有多个 线程同时要存取同一个堆,会引起更多的线程切换,足够引起极大的性能损失。现象如何发现多处理器系统存在这种性能损失?有一个简便的方法,打开“管理工具”中的“性 能”监视器,在系统组中添加一个上下文切换/秒计数,然后运行想要测试的多线程程序, 并且在进程组中添加该进程的处理器时间计数,这样就可以得到处理器在高负荷下要发生多 少次上下文切换。在高负荷下有上千次的上下文切换是正常的,但当计数超过80,000或 100,000时,说明 过多的时间都浪费在线程的切换,稍微计算一下就可以知道,如果每秒有 100,000 次线程切 换,则每个线程只有10微秒用于运行,而NT上的正常的时间片长度约有12毫秒,是前者 的上千倍。图1的性能图显示了过度的线程切换,而图2显示了同一个进程在同样的环境下,在使用 了下面提供的解决方法后的情况。图1 的情况下,系统每秒钟要进行120,000次线程切换, 改进后,每秒钟线程切换的次数减少到1,000次以下。两张图都是在运行同一个测试程序时 截取得,程序中同时有3个线程同时进行最大为2, 048字节的堆的申请,硬件平台是一个 双 Pentium II 450 机器,有 256MB 内存。解决方法本方法要求多线程程序是用VC+编写的,并且是动态链接到C运行库的。要求NT系统所 安装的VC+运行库文件msvcrt.dll的版本号是6,所安装的service pack的版本是5以上。 如果程序是用VC+ v6.0以上版本编译的,即使多线程程序和libcmt.lib是静态链接,本 方法也可以使用。当一个VC+程序运行时,C运行库被初始化,其中一项工作是确定要使用的堆管理器,VC+ v6.0 运行库既可以使用其自己内部的堆管理函数,也可以直接调用操作系统的堆管理函数 (HeapAlloc()系列的函数),在_heap_selec t()函数内部分执行以下三个步骤:1、检查操作系统的版本,如果运行于NT,并且主版本是5或更高(Window 2000及以后 版本),就使用 HeapAlloc()。2、查找环境变量_MSVCRT_HEAP_SELECT,如果有,将确定使用哪个堆函数。如果其值是 _GLOBAL_HEAP_SELECTED,则会改变所有程序的行为。如果是一个可执行文件的完整路径, 还要调用GetModuleFileName()检查是否该程序存在,至于要选择哪个堆函数还要查看逗号 后面的值, 1 表示使用 HeapAlloc(), 2 表示使用 VC+ v5 的堆函数, 3表示使用 VC+ v6 的堆函数。3、检测可执行文件中的链接程序标志,如果是由VC+ v6或更高的版本创建的,就使用 版本6的堆函数,否则使用版本5的堆函数。那么如何提高程序的性能?如果是和msvcr t.dll动态链接的,保证这个dll是1999年2 月以后,并且安装的service pack的版本是5或更高。如果是静态链接的,保证链接程序 的版本号是6或更高,可以用quickview.exe程序检查这个版本号。要改变所要运行的程序 的堆函数的选取,在命令行下键入以下命令:set _MSVCRT_HEAP_SELECT=_GLOBAL_HEAP_SELECTED,1以后,所有从这个命令行运行的程序,都会继承这个环境变量的设置。这样,在堆操作时 都会使用HeapAllocO。如果让所有的程序都使用这些速度更快的堆操作函数,运行控制面 板的“系统”程序,选择“环境”,点取“系统变量”,输入变量名和值,然后按“应用” 按钮关闭对话框,重新启动机器。按照微软的说法,可能有一些用VC+ v6以前版本编译程序,使用VC+ v6的堆管理器会 出现一些问题。如果在进行以上设置后遇到这样的问题,可以用一个批处理文件专门为这个 程序把这个设置去掉,例如:set _MSVCRT_HEAP_SELECT=c:program filesmyappmyapp.exe,1 c:inuggyapp.exe,2 测试为了验证在多处理器下的效果,编了一个测试程序heap test .c。该程序接收三个参数, 第一个参数表示线程数,第二个参数是所申请的内存的最大值,第三个参数每个线程申请内 存的次数。#define WIN32_LEAN_AND_MEAN#include #include #include #include /* compile with cl /MT heaptest.c */* to switch to the system heap issue the following command before starting heaptest from the same command line set _MSVCRT_HEAP_SELECT=_GLOBAL_HEAP_SELECTED,1 */* structure transfers variables to the worker threads */ typedef struct tDataint maximumLength;int allocCount; threadData;void printUsage(char* argv)fprintf(stderr,”Wrong number of parameters.Usage:”);fprintf(stderr,”%s threadCount maxAllocLength allocCount”,argv0);exit(1);unsigned _stdcall workerThread(void* myThreadData)int count; threadData* myData; char* dummy; srand(GetTickCount()*GetCurrentThreadId(); myData=(threadData*)myThreadData;/* now let us do the real work */ for(count=0;countallocCount;count+)dummy=(char*)malloc(rand()%myData-maximumLength)+1); free(dummy);_endthreadex(0);/* to satisfy compiler */ return 0;int main(int argc,char* argv)int threadCount;int count;threadData actData;HANDLE* threadHandles;DWORD startTime;DWORD stopTime;DWORD retValue;unsigned dummy;/* check parameters */if(argc4)printUsage(argv);/* get parameters for this run */threadCount=atoi(argv1);if(threadCount64)threadCount=64;actData.maximumLength=atoi(argv2)-1;actData.allocCount=atoi(argv3); threadHandles=(HANDLE*)malloc(threadCount*sizeof(HANDLE); printf(“Test run with %d simultaneous threads:”,threadCount);startTime=GetTickCount();for(count=0;countthreadCount;count+)threadHandlescount=(HANDLE)_beginthreadex(0,0,&workerThread, (void*)&actData,0,&dummy); if(threadHandlescount=(HANDLE)-1)fprintf(stderr,”Error starting worker threads.”); exit(2);/* wait until all threads are done */retValue=WaitForMultipleObjects(threadCount,threadHandles ,1,INFINITE);stopTime=GetTickCount();printf(“Total time elapsed was: %d milliseconds”, stopTime-startTime);printf(“ for %d alloc operations.”, actData.allocCount*threadCount);/* cleanup */ for(count=0;countthreadCount;count+) CloseHandle(threadHandlescount); free(threadHandles);return 0;测试程序在处理完参数后,创建参数1指定数量的线程,threadData结构用于传递计数 变量。workThread中进行内存操作,首先初始化随机数发生器,然后进行指定数量的malloc() 和free()操作。主线程调用WaitForMultipleObject()等待工作者线程结束,然后输出线程 运行的时间。计时不是十分精确,但影响不大。为了编译这个程序,需要已经安装VC+v6.0程序,打开一个命令行窗口,键入以下命令:cl /MT heaptest.c/MT表示同C运行库的多线程版静态链接。如果要动态链接,用/MD。如果VC+是v5.0 的话并且有高版本的msvcrt.dll,应该用动态链接。现在运行这个程序,用性能监视器查 看线程切换的次数,然后按上面设置环境参数,重新运行这个程序,再次查看线程切换次数。当截取这两张图时,测试程序用了 60,953ms进行了 3,000,000次的内存申请操作,使用 的是VC+ v6的堆操作函数。在转换使用HeapAlloc()后,同样的操作仅用了 5,291ms。在 这个特定的情况下,使用HeapAlloc()使得性能提高了 10倍以上!在实际的程序同样可以 看到这种性能的提升。结论多处理器系统可以自然提升程序的性能,但如果发生多个处理器争用同一个资源,则可能 多处理器的系统的性能还不如单处理器系统。对于C/C+程序,问题通常发生在当多个线程 进行频繁的内存操作活动时。如上文所述,只要进行很少的一些设置,就可能极大地提高多 线程程序在多处理器下的性能。这种方法即不需要源程序,也不需要重新编译可执行文件, 而最大的好处是用这种方法得到的性能的提高是不用支付任何费用的
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 办公文档 > 模板表格


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!