📄 csdn_文档中心_win32 多线程的性能(2).htm
字号:
个计算的测试程序,那么您应该提高该值,使它大于或等于运行您的测试程序所需要的最大并发数。<BR><BR> 让我们看一下处理器和终结器,以得到精确测量的结果:<BR><BR><BR>extern
"C" <BR><BR>{<BR><BR>long WINAPI pProcessor(long
iArg)<BR><BR>{<BR><BR>PTHREADBLOCKSTRUCT
ptArg=(PTHREADBLOCKSTRUCT)iArg;<BR><BR>BOOL
bResult=TRUE;<BR><BR>int iDelay=(ptArg->iDelay);<BR><BR>if
(bCPUBound) <BR><BR>{<BR><BR>int
iLoopCount;<BR><BR>iLoopCount=(int)(((float)iDelay/1000.0)*ptArg->tbOutputTarget->m_iBiasFactor);<BR><BR>QueryPerformanceCounter(&ptArg->liStart);<BR><BR>for
(int iCounter=0; iCounter<iLoopCount;
iCounter++);<BR><BR>}<BR><BR>else<BR><BR>{
<BR><BR>QueryPerformanceCounter(&ptArg->liStart);<BR><BR>Sleep(ptArg->iDelay);<BR><BR>};<BR><BR>return
bResult;<BR><BR>}<BR><BR><BR>long WINAPI pTerminator(long
iArg, long iReturnCode)<BR><BR>{<BR><BR>PTHREADBLOCKSTRUCT
ptArg=(PTHREADBLOCKSTRUCT)iArg;<BR><BR>QueryPerformanceCounter(&ptArg->liFinish);<BR><BR>ptArg->iEndOrder=iEndIndex++;<BR><BR>return(0);<BR><BR>}<BR><BR><BR>}<BR><BR><BR> 处理器模拟一个计算,其长度已经被放到一个与计算有关的数据结构
THREADBLOCKSTRUCT 中。THREADBLOCKSTRUCT
保持着与计算有关的数据,如其延迟和终止时间(以性能计数“滴答”来衡量),以及反向指针,指向实用化该结构的视图(view)。<BR><BR> 通过简单的使计算“睡眠”指定的时间就可以模拟基于I/O的计算。基于
CPU的计算将进入一个空的 for 循环。这里的一些注释是为了帮助理解代码的功能:计算是基于 CPU
的,并且假定其执行时间为指定的毫秒数。在本测试程序的早期版本中,我仅仅是要 for
循环执行足够多的次数以满足指定的延迟的需求,而不考虑数字的实际含义。(根据相关的代码,对于基于I/O的计算该数字实际意味着毫秒,而对于基于CPU的计算,该数字则意味着迭代次数。)但是,为了能够使用绝对时间来比较基于CPU的计算和基于I/O的计算,我决定重写这段代码,这样无论对于基于CPU的计算还是基于I/O的计算,与计算有关的延迟都是以毫秒测量。<BR><BR> 我发现对于具有指定的、预先定义时间长度的基于CPU的计算,要编写代码来模拟它并不是一件简单的事情。原因是这样的代码本身不能查询系统时间,因为所引发的调用迟早都会交出
CPU,而这违背了基于 CPU 的计算的要求。试图使用异步多媒体时钟事件同样没有得到满意的效果,原因是 Windows NT
下计时器服务的工作方式。设置了一个多媒体计时器的线程实际上被挂起,直到该计时器回调被调用;因此,基于 CPU
的计算突然变成了基于 I/O
的操作了。<BR><BR> 于是,最后我使用了一个有点儿低劣的技巧:CThreadLibTestView::OnCreate
中的代码执行 100 次循环从 1 到 100,000 计数,并且取样通过该循环所需要的平均时间。结果被保存在成员变量
m_iBiasFactor
中,该成员变量是一个浮点数,它在处理器函数中被使用来决定毫秒如何被“翻译”成迭代次数。不幸的是,因为操作系统的高度戏剧性的天性,要决定实际上运行一个指定长度的计算要迭代多少次给定的循环是困难的。但是,我发现该策略在决定基于CPU的操作的计算时间方面,完成了非常可信的工作。<BR><BR><BR>注意
如果您重新编译生成该测试应用程序,请小心使用最优化选项。如果您指定了 "Minimize execution time"
优化,则编译程序将检测具有空的主体的 for
循环,并且删除这些循环。<BR><BR><BR> 终结器非常简单:当前时间被取样并保存在计算的
THREADBLOCKSTRUCT 中。在测试结束之后,该代码计算执行 ExecuteTest
的时间和终结器为每一个计算所调用的时间之间的差异。然后,所有计算所消耗的时间由所有已完成的计算中最后一个计算完成时所消耗的时间所决定,而响应时间则是每一个计算的响应时间的平均值,这里,每一个响应时间,同样,定义为从测试开始该线程消耗的时间除以该线程的延迟因子。请注意,终结器在主线程上下文中串行化的运行,所以在共享的
iEndIndex
变量上的递增指令是安全的。<BR><BR> 这些实际就是本测试的全部;其余的部分则主要是为测试的运行设置一些参数,以及对结果执行一些数学计算。填充结果到
Microsoft Excel 工作单中的相关逻辑将在"Interacting with Microsoft Excel:
A Case Study in OLE
Automation."一文中讨论。<BR><BR><BR>结果<BR><BR><BR> 如果您想要在您的计算机上重新创建该测试结果,您需要做以下的事情:<BR><BR> 如果您需要改变测试参数,例如最大计算数或协议文件的位置,请编辑
THRDPERF 示例工程中的
Threadlibtestview.cpp,然后重新编译生成该应用程序。(请注意,要编译生成该应用程序,您的计算机需要对长文件名的支持。)<BR><BR> 请确保文件
Thrdlib.dll 在一个 Threadlibtest.exe 能够链接到它的位置。<BR><BR> 如果您想要使用
Microsoft Excel 来查看测试的结果,请确定 Microsoft Excel
已经正确地被安装在运行该测试的计算机上。<BR><BR> 从 Windows 95 或 Windows NT 执行
Threadlibtest.exe,并且从“Run performance tests”菜单选择"Run entire
test
set"。正常情况下,测试运行一次要花费几个小时才能完成。<BR><BR> 在测试结束之后,检查结果时,既可以使用普通文本协议文件
C:\Temp\Results.fil ,也可以使用工作单文件
C:\Temp\Values.xls。请注意,Microsoft Excel
的自动化(automation)逻辑并不自动地为您从原始数据生成图表,我使用了几个宏来重新排列该结果,并且为您生成了图表。我憎恨数字(number
crunching),但是我必需称赞 Microsoft
Excel,因为即使象我一样的工作表妄想狂(spreadsheet-paranoid),也可以提供这样漂亮的用户界面,在几分钟之内把几列数据装入有用的图表。<BR><BR> 我所展现的测试结果是在一个
486/33 MHz 的带有 16 MB RAM 的系统收集而来的。该计算机同时安装了 Windows NT (3.51
版) 和 Windows
95;这样,在两个操作系统上的不同测试结果就是可比的了,因为它们基于同样的硬件系统。<BR><BR> 那么,现在让我们来解释这些值。这里是总结计算结果的图表;后面有其解释。该图表应该这样来看:每一个图表的
x 轴有 6 个值(除了有关长计算的消耗时间表,该表仅含有 5
个值,这是因为在我的测试运行时,对于非常长的计算计时器溢出了)。一个值代表多个计算;我以 2、5、8、11、14 和 17
个计算来运行每一个测试。在 Microsoft Excel
结果工作表中,您将会找到对于基于CPU的计算和基于I/O的计算的线程的每一种计算数目的结果,延迟(delay
bias)分别是 10 ms、30 ms、90 ms、270 ms,、810 ms 和 2430
ms,但是在该图表中,我仅包括了 10 ms 和 2430 ms
的结果,这样所有的数字都被简化,而且更容易理解。<BR><BR> 我需要解释 "delay bias."
的含义,如果一个测试运行的 delay bias 是 n,则每一个计算都有一个倍数 n 作为其计算时间。例如,如果试验的是
delay bias 为 10 的 5 个计算,则其中一个计算将执行 50 ms,第二个将执行 40 ms,第三个将执行
30 ms,第四个将执行 20 ms,而第五个将执行 10
ms。并且,当这些计算被串行执行时,假定为最糟的情况,所以具有最长延迟的计算首先被执行,其他的计算按降序排列其后。于是,在“理想”情况下(就是说,计算之间没有重叠),对于基于CPU的计算来说,全部所需的时间将是
50 ms + 40 ms + 30 ms + 20 ms + 10 ms = 150
ms。<BR><BR> 对于消耗时间图表来说, y 轴的值与毫秒对应,对于响应时间图表来说,y
轴的值与相对(relative
turnaround)长度(即,实际执行所花费的毫秒数除以预期的毫秒数)相对应。<BR><BR><BR><BR><BR>图
1. 短计算消耗时间比较,在 Windows NT 下<BR><BR><BR><BR><BR>图 2.
长计算消耗时间比较,在 Windows NT 下<BR><BR><BR><BR><BR>图 3. 短计算响应时间比较,在
Windows NT 下<BR><BR><BR><BR><BR>图 4. 长计算响应时间比较,在 Windows NT
下<BR><BR><BR><BR><BR>图 5. 短计算消耗时间比较,在 Windows 95
下<BR><BR><BR><BR><BR>图 6. 长计算消耗时间比较,在 Windows 95
下<BR><BR><BR><BR><BR>图 7. 短计算响应时间比较,在 Windows 95
下<BR><BR><BR><BR><BR>图 8. 长计算响应时间比较,在 Windows 95
下<BR><BR><BR>基于 I/O 的任务<BR><BR><BR> 以消耗时间和 turnaround
时间来衡量,基于 I/O
的线程当并发执行时比串行执行要好得多。作为计算得一个功能,对于并发执行来说,消耗时间以线性模式递增,而对于串行执行来说,则以指数模式递增(有关
Windows NT 请参阅图 1 和 2,有关 Windows 95 请参阅图 5 和
6)。<BR><BR> 请注意,这个结论与我们前面对基于 I/O 的计算的分析是一致的,基于 I/O
的计算是多线程的优秀候选人,因为一个线程在等待 I/O 请求结束时被挂起,而这段时间该线程不会占用 CPU
时间,于是,这段时间就可以被其他的线程所使用。<BR><BR> 对于并发计算来说,平均响应时间是一个常数,对于串行计算来说,平均响应时间则线性递增(请分别参阅图
3、4、7 和
8)。<BR><BR> 请注意无论任何情况,只有少数几个计算执行的方案中,无论串行或并发的执行,无论测试参数如何设置,并没有什么明显的区别。<BR><BR><BR>基于
CPU 的任务<BR><BR><BR> 正如我们前面所提到的,在一个单处理器的计算机中,基于 CPU
的任务的并发执行速度不可能比串行执行速度快,但是我们可以看到,在 Windows NT
下线程创建和切换的额外开销非常小;对于非常短的计算,并发执行仅仅比串行执行慢
10%,而随着计算长度的增加,这两个时间就非常接近了。以响应时间来衡量,我们可以发现对于长计算,并发执行相对于串行执行的响应增益可以达到
50%,但是对于短的计算,串行执行实际上比并发执行更加好。<BR><BR><BR>Windows 95 和 Windows
NT 之间的比较<BR><BR><BR> 如果我们看一看有关长计算的图表(即,图2、4、6 和 8),我们可以发现在
Windows 95 和 Windows NT 下其行为是极其类似的。请不要被这样的事实所迷惑,即好象 Windows 95
处理基于I/O的计算与基于CPU的计算不同于 Windows
NT。我把这一结果的原因归结为这样一个事实,那就是我用来决定多少个测试循环与 1
毫秒相对应的算法(如前面所述)是非常不精确的;我发现同样是这个算法,在完全相同的环境下执行多次时,所产生的结果之间的差异最大时可达20%。所以,比较基于
CPU 的计算和基于 I/O 的计算实际上是不公平的。<BR><BR> Windows 95 和 Windows NT
之间不同的一点是当针对短的计算时。如我们从图1 和5 所看到的,对于并发的基于I/O的短计算,Windows NT
的效果要好得多。我把这一结果得原因归结为更加有效率得线程创建方案。请注意,对于长得计算,串行与并发I/O操作之间的差别消失了,所以这里我们处理的是固定的、相对比较小的额外开销。<BR><BR> 对于短的计算,以响应时间来衡量(如图
3 和 7),请注意,在 Windows NT 下,在10个线程处有一个断点,在这里更多的计算并发执行有更好的效果,而对于
Windows 95
,则是串行计算有更好的容量。<BR><BR> 请注意这些比较都是基于当前版本的操作系统得到的(Windows NT
3.51 版和 Windows
95),如果考虑到操作系统的问题,那么线程引擎非常有可能被增强,所以两个操作系统之间的各自行为的差异有可能消失。但是,有一点很有趣的要注意,短计算一般不必要使用多线程,尤其是在
Windows 95
下。<BR><BR><BR>建议<BR><BR><BR> 这些结果可以推出下面的建议:决定多线程性能的最主要因素是基于
I/O 的计算和基于 CPU
的计算的比例,决定是否采用多线程的主要条件是前台的用户响应。<BR><BR> 让我们假定在您的应用程序中,有多个子计算可以在不同的线程中潜在地被执行。为了决定对这些计算使用多线程是否有意义,要考虑下面的几点。<BR><BR> 如果用户界面响应分析决定某些事情应该在第二线程中实现,那么,决定将要执行的任务是基于I/O的计算还是基于CPU
的计算就很有意义。基于I/O的计算最好被重新定位到后台线程中。(但是,请注意,异步单线程的 I/O
处理可能比多线程同步I/O要好,这要看问题而定)非常长的基于CPU的线程可能从在不同的线程中被执行获益;但是,除非该线程的响应非常重要,否则,在同一个后台线程中执行所有的基于
CPU
的任务可能比在不同的线程中执行它更有意义。请记住在任何的情况下,短计算在并发执行时一般会在线程创建时有非常大的额外开销。<BR><BR> 如果对于基于CPU的计算
—
即每一个计算的结果只要得到了就立刻能应用的计算,响应是最关键的,那么,您应该尝试决定这些计算是否能够以升序排序,在此种情况下这些计算串行执行的整体性能仍然会比并行执行要好。请注意,有一些计算机的体系结构的设计是为了能够非常有效率地处理长的计算(例如矩阵操作),那么,在这样的计算机上对长的计算实行多线程化的话,您可能实际上牺牲了这种结构的优势。<BR><BR> 所有的这些分析都假定该应用程序是运行在一个单处理器的计算机上,并且计算之间是相互独立的。实际上,如果计算之间相互依赖而需要串行设计,串行执行的性能将不受影响(因为串行是隐式的),而并发执行的版本将总是受到不利的影响。<BR><BR> 我还建议您基于计算之间相互依赖的程度决定多线程的设计。在大多数情况下子计算线程化不用说是好的,但是,如果对于拆分您的应用程序为多个可以在不同线程处理的子计算的方法有多种选择,我推荐您使用同步的复杂性作为一个条件。换句话说,如果拆分成多个线程而需要非常少和非常直接的同步,那么这种方案就比需要使用大量且复杂的线程同步的方案要好。<BR><BR> 最后一个请注意是,请牢记线程是一种系统资源,不是无代价的;所以,there
may be a greater penalty to multithreading than performance
hits alone. 作为第一规则(rule of
thumb),我建议您在使用多线程时要保持理智并且谨慎。在多线程确实能够给您的应用程序设计带来好处的时候才使用它们,但是,如果串行计算可以达到同样的效果,就不要使用它们。<BR><BR><BR>总结<BR><BR><BR> 运行附加的性能测试套件产生了一些特殊的结果,这些结果提供了许多有关并发应用程序设计的内部逻辑。请注意我的许多假定是非常基本的;我选择了比较非常长的计算和非常短的计算,我假定计算之间完全独立,要么完全是基于I/O的计算,要么是完全基于CPU的计算。而绝大多数的现实问题,如果以计算长度和
boundedness
来衡量,都是介于这两种情况之间的。请把本文中的材料仅仅作为一个起点,它使您更加详细地分析您的应用程序,以决定是否使用多线程。<BR><BR> 在本系列的将来的一篇文章中,我将讨论通过异步I/O来增强基于I/O的操作的性能。<BR></TD></TR></TBODY></TABLE></DIV>
<P> </P><BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE align=center bgColor=#006699 border=0 cellPadding=0 cellSpacing=0
width=770>
<TBODY>
<TR bgColor=#006699>
<TD align=middle bgColor=#006699 id=white><FONT
color=#ffffff>对该文的评论</FONT></TD>
<TD align=middle>
<SCRIPT src="CSDN_文档中心_Win32 多线程的性能(2).files/readnum.htm"></SCRIPT>
</TD></TR></TBODY></TABLE><BR>
<DIV align=center>
<TABLE align=center bgColor=#cccccc border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TH bgColor=#006699 id=white><FONT
color=#ffffff>我要评论</FONT></TH></TR></TBODY></TABLE></DIV>
<DIV align=center>
<TABLE border=0 width=770>
<TBODY>
<TR>
<TD>你没有登陆,无法发表评论。 请先<A
href="http://www.csdn.net/member/login.asp?from=/Develop/read_article.asp?id=2841">登陆</A>
<A
href="http://www.csdn.net/expert/zc.asp">我要注册</A><BR></TD></TR></TBODY></TABLE></DIV><BR>
<HR noShade SIZE=1 width=770>
<TABLE border=0 cellPadding=0 cellSpacing=0 width=500>
<TBODY>
<TR align=middle>
<TD height=10 vAlign=bottom><A
href="http://www.csdn.net/intro/intro.asp?id=2">网站简介</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=5">广告服务</A> - <A
href="http://www.csdn.net/map/map.shtm">网站地图</A> - <A
href="http://www.csdn.net/help/help.asp">帮助信息</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=2">联系方式</A> - <A
href="http://www.csdn.net/english">English</A> </TD>
<TD align=middle rowSpan=3><A
href="http://www.hd315.gov.cn/beian/view.asp?bianhao=010202001032100010"><IMG
border=0 height=48 src="CSDN_文档中心_Win32 多线程的性能(2).files/biaoshi.gif"
width=40></A></TD></TR>
<TR align=middle>
<TD vAlign=top>百联美达美公司 版权所有 京ICP证020026号</TD></TR>
<TR align=middle>
<TD vAlign=top><FONT face=Verdana>Copyright © CSDN.net, Inc. All rights
reserved</FONT></TD></TR>
<TR>
<TD height=15></TD>
<TD></TD></TR></TBODY></TABLE></DIV>
<DIV></DIV><!--内容结束//--><!--结束//--></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -