|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
在1995年5月23日以“Java”的名称正式发布了。
<br>分派大批小型类对象(如:10,000小型纪录)最快和最好办法是甚么?固然,MFC序列流化对象能够完成所需的义务。可是,内存的分派和烧毁相称耗时。有无举措对此举行改善?
DaveKerrison
<br>我没法告知你最好的办法,由于那取决与使用程序的详细情形和其利用体例。功能和内存分派是云云伟大的一个主题,有关它们已有良多良多书本。没有哪种计划合适一切的情况。最优化老是必要在速率和别的资本之间举行明智的衡量。比方,假如你乐意创建巨型索引,那末就会取得十分快的查询速率。大概要想显现速率快,那末就得以加载工夫作为价值。因而,本文我只能就某些必要思索的成绩给你供应一个概述,和供应一些工具和路子以匡助你本人找到谜底。
假如你以为程序的功能不太中意,起首必需断定瓶颈在哪,对此要有苏醒的熟悉。你能够借助庞大的工具(profiler)来发生各类有关功能的呈报,但假如只是想晓得你的代码在那里耗时,那末用一些本人编写的复杂工具便可,我写了一个类叫ShowTime,它能够呈报代码的某些部分实行时要消费多长工夫。为了利用它,你只需在要用时钟的代码块肇端处实例化一个ShowTime仓库对象便可:- voidCalculatePi(){ShowTimest("Calculatingpi");//doit}
复制代码 这段代码将发生一个象上面如许的TRACE信息: ShowTime是怎样事情的呢?它为智能指针和在代码块肇端处和开端处你想做某些主动处置的中央利用罕见的C++机关函数/析构函数(ctor/dtor)形式。ShowTime的机关函数将时钟工夫(自从历程启动后的时钟嘀嗒数)保留在某个数据成员中;析构函数则用从最初的时钟数中减往这个时钟数并发生一条信息。因为机关函数/析构函数是在代码块的肇端处/开端处挪用的,如许便测算出统共用了几工夫。代码如Figure1所示。
ShowTime其实不太庞大。好比,它其实不思索多线程的情形,而且也不呈报在每一个函数中某个工具损耗了几工夫。可是关于一样平常利用来讲,它能给你供应使用程序在那边耗时的很好的参考。不要健忘针对Release版本举行功能测试!究竟那是你托付利用的版本。别的,Release和Debug版本之间的不同大概会歪曲你的了局。比方,依附你的设置体例,debug版本大概要举行分外的仓库反省,如许便使使用程序功能下落。因为在Release版本中没有TRACE信息,以是我增加了别的一个类,PerfLog,它能够将功能统计定向到一个文本文件:- //openlogfilePerfLogmylog("MyResults.log");
复制代码 如今ShowTime能够将信息写进MyResults.log文件和TRACE流。可是,不要忘了在托付程序之前往失落这本性能监督。
有了ShowTime在手,我能够入手下手回覆你的成绩了。我写了一个小程序,PerfTest,这是一个典范的具有文档的MFC文档/视图使用,它利用三种分歧的办法分派具有20,000条定长纪录的链表。
办法一是典范的MFC体例。链表的完成利用MFC的CObList,链表中的每一个项目利用独自的表单位。每一个表单位只是小小布局,此布局保留指向高低单位的指针和对象自己。以是CObList的每项由12个字节的开支,可是,假如你必要几个表指向不异项目标话,就必需要有几个表单位。(比方,你想用分歧的办法排序对象)。
办法二暗示了第一本性能上的改善。这里纪录自己存储下一笔记录的指针,以是没有独自的表单位。这个办法在唯一一个链表的情形下才成为大概——也就是说,假如你不必要用几个链表来指向以分歧体例排序的不异对象。在如许情形下,利用数组大概更无效。但即使是一个链表,假如要常常修正按次,链表也比数组要快,由于修正指针比在内存中挪动对象要快。
办法一和办法二都是每分派一个纪录/对象独自挪用一次new操纵符。假如你分派20,000个对象,便挪用20,000次new操纵。办法三用单个数组一次性分派一切的20,000个对象:
- m_array=newCMyRecord[20000];
复制代码 纪录的链接则是经由过程设置每笔记录中指向下一笔记录的指针域完成的。分派的速率快,由于只要一次函数挪用,但它必要一块一连的足以包容20,000笔记录的内存块。固然,编译器仍旧要包管对象的初始化。当你用向量情势的new操纵,编译器发生代码来挪用每一个对象的机关函数,因而有20,000次的机关函数挪用。一样,在delete[]操纵中会有20,000次的析构函数挪用。假如机关函数/析构函数都为空,这些挪用将被优化失落。但假如它们有实践的事可做,这个代码将必要无限次地实行。这时候,你大概要进一步经由过程给该数组分派原始字节来减速功能(制止机关函数/析构函数挪用),然后用手工编写代码来初始化这些对象——但如今这个对你已不成成绩。
另有一种举措是全部类的分派功能改善战略——我只将它提出来,不作进一步的切磋——这个办法重载new操纵符,以下所示:- classCMyRecord{public:void*operatornew(size_tnbytes){returnFancyAlloc(nbytes);}voidoperatordelete(void*p){FancyFree(p);}};
复制代码 你能够依照本人的志愿完成FancyAlloc和FancyFree,只需它们依照准确的巨细分派/开释内存块。假如你有一个在程序中全程利用的特别对象,最经常使用的技能之一是保护一个开释(freepool)对象池。而不是往挪用free,你的delete操纵符将开释的对象增加到一个叫开释池的链表中。然后分派器挪用malloc之前在此开释池中查找对象。如许做可使分派/开释操纵极为疾速,但你必需当心行事,利用内建的分派器而不克不及越雷池一步,年夜多半情形下它体现得相称不错。
不论如何,就像我入手下手所说的那样,有关内存分派的具体内容已超越本专栏会商的局限。你可使用的技能不可胜数。我之以是选择这三种办法,并非由于它们怎样棒,而是举例申明你能够用来优化程序的几种体例。
为了弄分明哪一种办法最快,关头的成绩是:你要甚么工具快?假如你存眷的是内存分派,那末你以为办法一(CObList)最慢,由于它分派的对象最多,而办法三(成块的分派)最快,由于它一次性地分派。可是关于读写操纵又怎样呢?每种办法都表示了分歧的序列化战略。关于办法一和办法二来讲,每笔记录独自挪用CMyRecord::Serialize来序列化(拜见Figure2),关于办法三而言,我
猛的一下写进全部数组。还要申明一点,这是个很果断的做法,我只用它了做教导目标。就像办法二那样,我能够轻松地像序列化单笔记录那样序列化巨型数组。注重在完成非正统的序列化战略时,做起来会略微庞大一些。
任什么时候候序列化包括指针的数据时,必需要将指针转换成在磁盘上成心义的工具,由于数据被实践加载到的地位与下次读取该文件的内存地位恰好完整不异的概率长短常低的。就像你不买彩票而想中奖的概率一样。MFC完成了很多奇妙的操纵对指针和磁盘IDs举行往返转换。关于PerfTest,我以链表按次保留纪录,因而不必要IDs。我能够复杂地在它被加载落后行重链(办法二和办法三)。固然,这意味着假如你改动链表按次,则办法三将失利(我已隐含假定该链表就是一个数组)。
最初,另外一个序列化成绩是:你盘算序列化的纪录是定长的仍是可变长度的?CMyRecord含有64个字符的数组。CMyRecord::Serialize利用CArchive::ReadString/WriteString来序列化用到的字符,而不是一切的64个字符。假如字符串是“foo”,则它只序列化4个字符(“foo”加开头必需的“”)。办法三写进全部数组。它序列化一切字符,即便字符串为空。如许是否是很华侈呢?要看情形。假如字符串是10个字符的德律风号码或16位字符的信誉卡号码,年夜多半纪录大概城市补足字符,因而序列化一切内容大概没甚么成绩。但假如字符串是一个地点或可选的字段,那末磁盘上大概会有成兆字节的零。那就好思索了。这不单单是磁盘空间的华侈,并且速率也会受影响,由于它要花更多的工夫来读/写更多的字节。办法三一次性读/写全部表——那它真的就更快吗?
为了找到谜底,我在PerfTest中利用ShowTime对象来显现分歧的操纵要花多长工夫。我运转这个程序,创立一个新文件,保留它又读取它,然前进出。Figure3显现了ShowTime发生的日记,
个中有注解注释其操纵。像希冀的那样,办法一(CObList)分派是最慢的(130ms),办法三最快(70ms)。烧毁开释对象不同更加分明。那末序列化又怎样?关于写进操纵,办法二和办法三不同不年夜——分离为60和61ms。明显一次性写操纵争夺的工夫是以写进太多半据为价值的——办法二是536KB,办法三是复杂的2.9MB。(我写了另外一个类,ShowFileUsed,这个类呈报序列化时代存档CFile入手下手和停止地位之间的不同)。关于读取操纵,办法三对照快,但同时也有一个磁盘缓冲反作用——这是当你动手功能测试时,另外一个必需思索的要素。
ShowTime供应原始的功能数据,但你必需对它们举行注释,以便普通易懂。利用CObList(办法一)分派所用的工夫几近是办法三年夜块数组分派所用工夫的两倍——但不知是不是有人注重到没有,它用70ms的工夫来翻开一个文件?从字面上讲是一眨眼的工夫。那年夜块读取所节俭的工夫的确值得以五倍的磁盘空间为价值吗?关于PerfTest来讲,谜底一定是,No。关于别的的一些使用,谜底多是Yes。底线是必需经由实验才干断定。你老是可使本人的程序更快,但一般只能以别的资本为价值,像内存、磁盘空间、庞大性(注释为牢靠性、强健性和程序员小时数)或别的方面的速率。功能优化是一种艺术。技能是充实了解你的使用程序,并购置或编写一些工具,这些工具是你能懂得使用程序究竟在干甚么。你大概会对所发明的事变感应惊奇。
<br>我正在进修Microsoft.NET框架,不太了解控件和组件之间的不同。我晓得这些术语能够互用,但甚么时分从Control派生,甚么时分从Component派生呢?
LindaBerno
<br>好成绩!复杂说来,控件就是具有效户界面的组件。要说的详细一点,就得回忆初期Windows的汗青本源,事先控件指任何子窗口——按钮、列表框、编纂框大概某个对话框中的静态文本。从观点上讲,这些窗口——控件——相似用来操纵收音机或小电器的旋钮和按钮。跟着控件数目的增添(组合框、日期工夫控件等等),控件渐渐成为子窗口的代名词,不管是用在对话框中仍是用在别的品种的主窗口中。没过量久BASIC程序员入手下手编写他们本人公用的控件,天然而然地人们便想到共享这些控件。共享代码的办法之一是经由过程磁盘拷贝,但那样明显效力低下。必需要有一种机制使开辟者创建的控件可以在别的程序员的使用中十拿九稳地拔出,这即是VBA控件,OLE控件,OCX和最初ActiveX控件的念头。
这就是控件和组件之间发生搅浑之地点。由于为懂得决控件的可复用成绩,一切这些手艺必需起首办理更加一样平常的组件重用成绩。(COM,假如你还记得它的话,意义是组件对象模子)。在软件行话中,组件这个术语指任何可复用的对象或任何可与别的对象交互的代码体。子程序的创造,已经一度成为程序员趋附者众的软件工程圣杯:一种一致的编程实际,它使程序员从基础构建块——也就是用所选言语编写的各类组件创建年夜型体系。从子程序演化到OOP,到DLLs,再到COM,再到.NET框架的每种新的编程典范都代表了一种分歧的供应可重用性的计划。VBX利用DLLs的固假名称。COM利用接口和IUnknown。.NET框架利用微软的两头言语(MSIL)层和大众言语运转时(CLR)来供应一致的粘合。
因而,控件是组件的一个次要样本(而且汗青上曾驱动着组件的开辟),控件又不单单是独一的一种组件。组件不必要显现任何信息或用户界面。组件大概完成迷信盘算,搜集功能数据,盘算1971年1月1日到如今的毫秒数,仰或是读取布什总统竞选举动保险箱里的美金数。Figure4显现了VisualStudio.NET中的非控件组件例子。
<br>
Figure4组件
在.NET框架中,术语控件和组件为.NET付与了专门的意义。Component类为被用于计划层面的对象如WindowsFormsDesigner(Windows窗体计划器)或WebFormsDesigner(Web窗体计划器)供应了基础完成。某个Component是任何能够被拽到某个窗体的任何工具。Component类完成IComponent,ISite和IContainer。这些接口比起其来自OLE时代的COM从兄弟要复杂很多。IContainer比起带有Add/Remove办法的组件列表和组件属性来要略微庞大一点,它取得的组件是一个ComponentCollection(组件汇合)。
IComponent从IDisposable派生而来,而且只要一个属性,Site,猎取组件的ISite接口。Component大概有,也大概没有Site。ISite有四个属性,个中包含Name和DesignMode,它把持该组件是不是处于——还能是甚么?——计划形式。ISite派生于另外一个接口,IServiceProvider,它只要一个办法,GetService。在COM中,IServiceProvider相似QueryInterface——用它能够经由过程ID来查询某个对象的接口,可是与QueryInterface分歧的是该对象自己不必往完成这个接口,它仅仅晓得在那里和怎样猎取它便可。一样,在.NET框架中,IServiceProvider是一种猎取别的接口或对象的通用办法——服务——对象不必完成它就晓得的一种服务。
.NET框架使得编写可复用组件轻松自若,不再必要IDL,不再必要范例界说言语,不再必要吃力的计划时撑持。经由过程反射(reflection)的邪术,CLR从代码自己就已晓得了该晓得的统统,一切的类都在掌控当中。为了增加计划时撑持,你只需用分外的计划器标志你的属性便可。比方,在托管C++中:- //inCMyControl[Category(S"Appearance")][Description(S"Specifieswidgetforegroundcolor.")]_propertyColorget_ForeColor(){...}_propertyvoidset_ForeColor(Colorvalue){...}
复制代码 如今窗体计划器在“表面”(Appearance)中列出你的ForeColor属性并利用匡助形貌(Description)。有关计划时属性的更多内容,请参考.NET框架文档中的“组件的计划时属性”
<br>
Figure5类条理布局
Figure5显现了.NET框架中的类条理布局,它能申明上述会商的成绩。正如你所看到的,Control从
Component派生而来。这是用别的一种体例来讲明控件即组件(反之则否则)。更详细地讲,控件是一个用用户界面的组件——能绘制工具并能与用户交互。Control类仍是一切托管窗口类的基类——窗体、按钮、栅格、面板、工具栏等等。Control类是界说WndProc和ClientSize和一切尺度窗口事务如GotFocus和Click的中央。Web控件(System.Web.UI.Control)也是组件,不外从严厉的意义上讲,它不是从System.ComponentModel.Component派生的。(关于Web控件,其名字空间为System.Web.UI,Control自己完成IComponent。)
除完成IComponent以外,System.ComponentModel.Component还供应了一切组件必要的列集撑持,但它是经由过程从MarshalByRefObject派生来完成的。假如想天生一个值列集组件,能够从MarshalByValueComponent派生(它完成了IComponent,IDisposable和IServiceProvider)。System.Data.DataColumn,DataSet和DataTable都是是值列集组件的例子。这些对象跨呆板/历程界限传送实在际数据。
假如你正在编写其别人也能用窗体计划器拖拽到其窗体的可重用的小组件,那末你必需从Component派生。假如你的小组件还具有用户界面——能创立窗口,绘画或与用户交互——那末就应当从Control派生。分明了吗?
向Paul发问和批评请发到cppqa@microsoft.com.
作者简介
PaulDiLascia是一位自在作家,参谋和Web/UI计划者。他是《WritingReusableWindowsCodeinC++》书(Addison-Wesley,1992)的作者。经由过程http://www.dilascia.com能够取得更多懂得。
本文出自MSDNMagazine的June2004期刊,可经由过程外地报摊取得,大概最好是定阅
本文由VCKBASEMTT翻译
原代码下载:CQA0406.exe(234KB)
原文出处:MSDNMagazineJun2004(C++Q&A)
原著:PaulDiLascia
翻译:Northtibet
其实产生见解的过程就是训练自己发现问题,分析问题的能力。根据以上的认识我想谈下传统的学习与通过视频独立学习的优缺点: |
|