鲁大师cpu跑分排行跑分高精密度事件计时器要关掉吗?

新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
丰衣足食, 积分 942, 距离下一级还需 58 积分
论坛徽章:2
个人理解是内核里面应该有2种类型的硬件中断定时器,称为普通定时器、高精度定时器hrtimer;
普通定时器的精度由HZ定义,ms级别(1ms/10ms),主要是维护系统tick其相关一些工作;
hrtimer的精度能够到ns级别,主要是为驱动、应用程序提供高精度定时;
有几个疑问:
1, 对于SMP(假设8个cpu),普通定时器会给每个cpu上都触发一个硬件中断吗? 系统的jffs这些是以哪个cpu来维护的?
2,HZ之所以定在ms级别,就是综合考虑了系统开销的,现在hrtimer的精度是ns级别,中断这么频繁的产生,系统开销这么大,能抗的住吗?
3,既然ns级别的系统开销抗的住,直接把HZ设置为ns级别就行了,干嘛再搞一个hrtimer出来?
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp
稍有积蓄, 积分 236, 距离下一级还需 264 积分
论坛徽章:0
现在的机器时钟频率,每纳秒能执行几条汇编指令就不错了。访问一次主存都要100纳秒啦。
巨富豪门, 积分 22811, 距离下一级还需 17189 积分
论坛徽章:15
这里只是精度是ns,实际如果每ns来一个定时器的话,这种开销应该承受不了,一般不会这么干吧。。。
丰衣足食, 积分 942, 距离下一级还需 58 积分
论坛徽章:2
是不是能用到rttimer高精度定时的,一般都是一次性的需求,不会说来一个50ns的周期性的定时需求,如果是周期性的,50ns一次中断,系统确实吃不消;
如果确实需要用到周期性的,那周期也不会太短,至少要几百ns了吧
白手起家, 积分 3, 距离下一级还需 197 积分
论坛徽章:0
关于第一个问题,我也有同样的困惑,请教一下楼主,不知道你是否有答案了,能否共享一下。谢谢
“对于SMP(假设8个cpu),普通定时器会给每个cpu上都触发一个硬件中断吗? 系统的jffs这些是以哪个cpu来维护的? ”
稍有积蓄, 积分 250, 距离下一级还需 250 积分
论坛徽章:0
我有同事碰到过,用了hrtimer严重影响了性能。
巨富豪门, 积分 20886, 距离下一级还需 19114 积分
论坛徽章:18
漏帖子了?记得前两天回答过的。
没记错的话,jiffies是由cpu0来统一维护的,这样实现策略会简单一些。
6楼能说的更详细一些么,是哪个子系统被影响了?
白手起家, 积分 31, 距离下一级还需 169 积分
论坛徽章:0
时钟中断的机制是有一个时钟比较器,你需要设置一个值给时钟比较器,然后时钟会按照tick一直往前走,当达到时钟比较器的值的时候,时钟比较器会触发一个中断。我的理解是hrtimer的精度就是tick的精度,而时钟中断之间的间隔是n*tick,而n应该是由clocksource和clockevent来定的。当前位置&&&&驱动信息
AMD 8000系列芯片组板载高精度事件计时器(High Precision Event Timer)最新驱动1.0.0.6 WHQL版For Win9x/2000/Server 年4月29日发布)
操作系统:WIN9X,WIN2000
发布厂商:
发布日期:
文件容量:4.57M
驱动种类:
适应硬件:
下载次数:次
手机下载带回去用?点开扫描二维码
分享给小伙伴们:
关注驱动精灵
这条解决了我的问题0条
这条驱动不太适合我0条
请输入反馈内容
请输入您的联系方式,QQ/邮箱/联系电话
您也可以通过以下方式和我们交流:
手机扫描进行关注
登录驱动之家
没有帐号?
用合作网站帐户直接登录软件排行榜
历史版本下载
AMD-8000芯片组是AMD公司专为Hammer系列处理器量身定制的支持新一代64位处理器的芯片组。该款驱动适用于采用AMD-8111 HyperTransport I/O hub芯片的平台
天极大视野
京公网安备84号采用MXT5611的高精度可配置定时电路
采用MXT5611的高精度可配置定时电路
采用MXT5611的高精度可配置定时电路在工业控制、家电应用、民用爆破、武器引信等领域,利用到很多时间类控制事件,这些时间类控制控制事件需要用到不同功能的定时器电路,这些时间控制事件要求的定时长度各不相同,需要的定时器数目也不一样,针对不同的应用领域,定时的进制、定时方式也不尽相同。在某些应用中,可能会利用到多个时间事件,但是时间长度不是很长,而别的应用中却是单个时间事件,但是要求时间长度很长。也有可能
采用MXT5611的高精度可配置定时电路在工业控制、家电应用、民用爆破、武器引信等领域,利用到很多时间类控制事件,这些时间类控制控制事件需要用到不同功能的定时器电路,这些时间控制事件要求的定时长度各不相同,需要的定时器数目也不一样,针对不同的应用领域,定时的进制、定时方式也不尽相同。在某些应用中,可能会利用到多个时间事件,但是时间长度不是很长,而别的应用中却是单个时间事件,但是要求时间长度很长。也有可能某些应用中定时方式是要求总共定时多长时间,而别的应用中要求定时方式为从某一时刻到另一时刻。总之,应用领域的不同,对定时电路的要求也不尽相同。这就要求定时器电路具备一定的配置功能,已满足该定时器电路在不同领域的应用。   本文设计的定时电路具备以下配置能力:时间长度、定时方式、定时进制、定时器的启动、定时达到后输出、数据通信方式等主要可配置项。    图1 可配置定时电路原理框电路内嵌128×16 B 容量EEPROM存储阵列,把需要处理的数据做为一个最小项,把EEPROM空间按地址顺序分为8个16×16 B存储窗口。其中固定一个窗口为功能配置信息存储窗口,其余窗口为定时数据存储窗口。功能配置信息中设置一个“有效定时数据存储窗口”控制字,可以把一些常见固定的定时出具写入EEPROM某些窗口中,如果需要进行某种固定的定时时,只需要把功能配置字中““有效定时数据存储窗口”控制字进行改写,即可立即进行需要的定时功能。EEPROM与寄存器组映射关系如图2。   电路在上电后,由上电引导模块把EEPROM中的配置信息载入到功能寄存器组中,再根据功能寄存器组配置状态(主要指“有效定时数据存储窗口控制字)把相应的定时数据载入到定时寄存器组中。完成上电引导过程后,可由DATAin进行电路配置更改和定时数据更新等功能,确认配置方式和定时数据无误后,可以通过电路定时启动端口发送启动电平脉冲或者由DATAin发送定时器启动祯来启动内部定时器,三个定时器可以同时或者分时启动,可根据控制需要进行启动。    定时器配置  表1 定时器配置字表1为定时器配置字,电路内每个定时器对应都对应一个定时器配置字,这样,可以根据应用定时要求,对三个定时器进行任一定时器的配置。同时要说明的是,定时器的启动方式配置和输出脉冲控制所定义的信号类型基本一致,这样就可以实现启动一个定时器定时完毕后立即启动另一个定时器的定时功能。   电路内含三个16位定时器,三个16位定时器之间进行一定规律的级联,以构成更大定时范围的定时器。配制方式如下图:    级联公式: 这样,电路可通过配制C0、C1、C2来使定时器实现不同方式的组合。当然,当C0、C1、C2都为1时,定时器电路将无外部时钟输入,此时电路不具备定时功能。  其中定时器0和定时器1的组成基本一致,以五个触发器(其中4个触发器处理正常计数功能,一个触发器处理进位、置数使能)作为“定时单元”,四个“定时单元”构成16位定时器,通过对“定时单元”定时过程中置数使能的产生和置数值的设定来设置不同的定时状态(如十进制定时则“定时单元”在从9减到0这个过程的下一个时钟周期为置数周期,产生置数使能信号,同时置数值设定为1001;而如果是六进制定时则“定时单元”在从5减到0这个过程的下一个时钟周期为置数周期,产生置数使能信号,同时置数值设定为0110。其他进制的定时过程同理。)通过对4个定时单元的不同设置,可产生全十进制定时方式,二进制定时方式,六十进制定时方式(应用于北京时间的分、秒处理)、二十四进制定时方式(应用于北京时间的小时处理)、三十进制定时方式(应用于北京时间的日处理,因为没有“0天”这种说法,故该处理需要在定时单元的结构上增加一个判定)。  在此结构上,如果需要增加新的定时进制处理,只需要对置数rom逻辑进行增加即可。定时器T2的设计是针对北京时间二进制表示的定时设计,同时具备基本的二进制定时。它主要处理北京时间小时级及小时以上的数据处理。做为基本二进制定时时,它可单独使用,也可以与定时器T0和定时器T1做级联使用。当它需要处理北京时间二进制表示高位信息处理时,和定时器T1组成32位定时器,定时器1处理“秒”、“分”信息,定时器T2处理“小时”、“天”“月”“年”数据。T2+T1的级联方式,主要用于处理从某一时刻到另一时刻的定时方式。   精度控制  电路提供两种可选择时钟源输入:内部集成硅振荡器和外部晶体振荡器输入。电路设计了两种精度控制方法,其一是时基脚准,时基校准就是解决因定时时钟误差引起的定时累计误差的办法。如果我们能得到一个精确的定时时钟,则没有这部分定时误差。但是不管怎么样的校准,始终是无法得到一个完全精确的定时时钟的,我们要做的是最大可能的得到一个精确定时时钟。  其二是定时校正,指在定时过程中,电路根据设定的某一固定值对定时过程进行校正或者接受外部校正信号对定时过程进行校正。这一方法旨在解决引非时钟误差问题引起的定时误差。同时,该校正方法也可以用于解决因时钟规律性偏差所引起的误差问题,比如时钟的温度漂移偏差问题。  硅振荡器的精度调整方法总的来说有两种:模拟方法和数字方法。模拟调整方法主要是在电路中测过程对电路中的电容阵列进行熔丝处理,调整电容值大小,得到一个较为精确的时钟,但是这种方法需要很大的成本,并且调整后的精度范围为1%左右,如果要得到更高精度的硅振荡器,则需要付出更大的成本,并且给电路设计带来很大的挑战。本电路提供一种更高精度的数字调整方法,该方法可以在电路正常工作前对电路进行在线校准,也可以在测试过程中对批电路进行一次性校准。  本电路的时基校准方法是通过外部端口输入标准512ms时间长度,以振荡器输出频率对512ms时间进行采样计数,得出一个计数值。然后把该计数值除以512,得到商值和余数。商值做为1ms时钟的基本长度,然后通过判断再次基本长度上增加或者不增加1个计数脉冲来得到最终的1ms时钟信号,这样每一个1ms输出时钟最大误差为1T(硅振荡器输出时钟周期),而512ms时间最大误差也为1T。上述判断过程以512位周期,即每一个512ms对商和余数做同样的处理。  那么,在不考虑温度等条件的情况下,以该方案得到的时钟进行Nms(N=512X+Y,X=0,1,2,3,……;0≤Y≤511)时间长度定时,最大误差为  (X+Y/4)·T。我们通过分析,可以得出以下几句数据:  438s时间长度定时误差为103T(T为内建振荡器输出时钟周期,当设计值为1us时,此时的定时精度约为2ppm)。  82m时间长度定时误差为104T(T为内建振荡器输出时钟周期,当设计值为1us时,此时的定时精度约为2ppm)。  142h时间长度定时误差为106T(T为内建振荡器输出时钟周期,当设计值为1us时,此时的定时精度约为2ppm)。  在不考虑硅振荡器的温度特性条件下,利用本方法产生时钟进行的定时精度为2ppm,可以等同于压控制式晶体振荡器频率精度的10^(-6)~10^(-5)量级。本电路给出的定时校正方法主要是用以解决定时过程中因外部环境所引起的定时偏差,或者解决因控制需要而改变定时时间长度的问题。电路在定时过程中,接受外部信号,对定时过程进行实时校正。     图4为定时器的结构示意图,以减计数器为核心,同时接受定时数据和修正数据。定时数据做为减计数器的定时起点,而修正数据主要是用来对进入定时器的定时时钟进行调整。定时器的修正功能模块可以保证,在任何一时刻,处理一帧数据,缓存一帧数据,让进入修正模块处理的数据完成后,缓存器中的数据立刻进入修正模块,而修正总线上的数据进入缓存器。  修正方法是根据外部修正数据的符号位进行增减判断,如果是增长定时过程N个时钟周期,则,在对定时时钟进行N个时钟周期的上升沿磨平处理,如果是缩短N个时钟周期,则在N个时钟周期内,定时器进入双沿触发定时过程。  图5为定时修正波形图,从ADJUST端口输入具体修正时间,图中第一帧为定时增长7个时钟周期,第二帧为定时缩短7个时钟周期。CLKIN为定时时钟,CLKOUT为修正后时钟。T0counter[15:0]为定时器的数据。  功耗考虑   根据电路的工作状态,可以把电路分为配置工作状态和定时状态。任意工作状态下,并非所有的模块都处于工作状态下。在设计模块间接口信号时,同时设计模块电源控制信号。在进行配置工作状态下,通信模块和储存单元处于工作状态,而整个定时处理工作模块处于等待状态。在这段时间,定时处理工作模块一直处于清零状态,而且对定时器时钟信号进行锁定不工作。当处于定时状态下,大部分的通信模块及存储单元不会发生数据变化,此时可以关断EEPROM的参考电流源来降低电路功耗。  结论  通过本文的配置方案和精度方案,可以保证电路定时精度在2ppm左右。采用菊花链式配置定时器结构,可以通过简单的配置得到多种定时应用,满足多种控制要求。
型号/产品名
长沙幻音电子科技有限公司
长沙幻音电子科技有限公司
长沙幻音电子科技有限公司
长沙幻音电子科技有限公司
长沙幻音电子科技有限公司用户名:tonyxiaohome
文章数:244
评论数:1996
访问量:794780
注册日期:
阅读量:1297
阅读量:3317
阅读量:461712
阅读量:1145990
51CTO推荐博文
这应该算我近期的工作了。我最近在做一个HashCache,里面有个回写功能,即将有更新的节点内容写入磁盘。这带来一个问题,这个HashCache是多线程访问环境,必须考虑加锁,由于HashCache内部有淘汰机制,需要利用到一个最近访问的时间戳来判定,也就是说,无论读还是写操作,最终都会对这个时间戳实现写动作,因此,不存在单写多读锁的调用可能性,只能用普通锁。
也就是说,这个HashCache,必须通过锁域封装,每个客户来访问的动作都是排他的,HashCache每次只能由一个客户访问。但这会带来效率问题,由于多任务开发的“大锁”问题,必然效率低下。但这又是维护正常逻辑必须的,没有办法折中。唯一的解决之道,就是尽量缩减每个访问动作的时间,使每个锁域粒度最小化,大家快进快出,保证效率。
这其实也是我使用Hash来做这个Cache的原因,Hash的访问复杂度,基本上是O(1)的,这保证我每个锁域都是O(1)的宽度,这使我的HashCache获得了最大交换能力,也就是访问能力最大化。
但是,回到开始的问题,我必须考虑回写逻辑,以一个线程不断遍历Hash表的每个节点,把内容有更新的节点,写回磁盘,这个动作,由于涉及磁盘交互,必然是一个高loading的动作,肯定不是O(1)啦,这没有办法,那我就得想个什么办法来控制这个回写逻辑的时间片不至于太宽,免得把锁占据太久,导致上层正常的访问逻辑被长时间挂住,甚至超时报错。
这就要求我必须在毫秒级控制时间片的精度。而且,误差不能太大,我预设大约每次回写线程工作5ms就退出,等待15ms再继续,这样我可以以20ms为周期,每秒钟完成50个周期左右,外部业务线程获得750ms左右的时间,而回写线程获得250ms的时间,各自完成自己的业务。我试图通过这种对时间片的精确控制,来保证业务访问的精度要求。
不过,这就有点麻烦了,我的工程库,在《0bug-C/C++商用工程之道》中,大家可以看到一个CDeltaTime的类,这是我常用的一个计时器(5.5.3小节,P194页),不过呢,这个计时器有个缺点,精度只有1秒,这显然不够,我没办法,昨天就重写了一个高精度计时器,不过一写起来才发现,这个问题没想的那么简单,呵呵,本来想喝口绿茶,结果被灌了一口二锅头。
我花了整整一个下午来做这件事情,中间呢,出于工程化开发的原则,我考虑了很多细节,我想了一下,还是写篇博文把这个过程展示一下,也许能给大家一点启发也说不定哈。
这个计时器,我本意还是仿照CDeltaTime类来做一个统计性的类,内部计算DeltaTime,供外部查询。在《0bug-C/C++商用工程之道》的P193页,我呢,有个CCountSub的类,它能处理unsigned long的数值,看起来还可以,但我仔细一想,发现一个问题。32bits的OS下,unsigned long的范围是4Bytes,这个用来存秒呢,可以存几十年的,不过,要是存毫秒,完蛋了,我算了一下,差不多49天就溢出,这肯定不行。没办法,我首先重新做了个统计模块,专门处理溢出问题。
class&CDWORDCount &&
public: &&
&&&&CDWORDCount(){Reset();} &&
&&&&~CDWORDCount(){} &&
&&&&DWORD&Reset(DWORD&dwValue=0){return&m_dwBegin=m_dwEnd=dwV} &&
&&&&DWORD&SetBegin(DWORD&dwValue){return&(m_dwBegin=dwValue);} &&
&&&&DWORD&SetEnd(DWORD&dwValue){return&m_dwEnd=dwV} &&
&&&&DWORD&GetBegin(void){return&m_dwB} &&
&&&&DWORD&GetEnd(void){return&m_dwE} &&
&&&&DWORD&GetX(void) &&
&&&&&&&&if(m_dwEnd&m_dwBegin) &&
&&&&&&&&{ &&
&&&&&&&&&&&&&&
&&&&&&&&&&&&int&nBeginX=abs(0-m_dwBegin);&&&&&&&&&
&&&&&&&&&&&&return&m_dwEnd+(DWORD)nBeginX;&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&} &&
&&&&&&&&return&(m_dwEnd-m_dwBegin); &&
private: &&
&&&&DWORD&m_dwB &&
&&&&DWORD&m_dwE &&
大家注意GetX这个函数,是求Begin和End的差值,由于windows下,求毫秒的函数GetTickCount返回的是DWORD,因此,我这个工具类也主要处理DWORD这个类型。具体函数意思我不用写注释了吧,看英文都看得懂。
当然,按我团队的规矩,还是要给一个测试函数,大家可以看看其对溢出逻辑的处理是正确的:
inline&void&TestCDWORDCount(void) &&
&&&&CDWORDCount&C &&
&&&&Count.SetBegin(0xFFFFFFFF); &&
&&&&Count.SetEnd(5);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&printf("%ld\n",Count.GetX()); &&
&&&&printf("%ld\n",abs(0-0xFFFFFFFF)); &&
ok,有了这个工具,我准备开始写计时器,我先完成了基本部分,嗯,大家注意,为了保证精度,这里面全部使用的是C++的内联函数写法,即函数会在编译时在调用处展开,节约压栈、弹栈的动作开销,我们这是在计时,省一点总是好的。
先给大家一个宏吧,这是求毫秒级精度的系统函数的跨平台封装。《0bug-C/C++商用工程之道》里面有,各位读者可以看看。
#define&_GetTimeOfDay&GetTickCount &&
由于写这个计时器一波三折,我也是分几次才写好,因此,这里先给基本类,后面再逐渐介绍几套计时方案。
class&CTonyDeltaMicroSeconds &&
public: &&
&&&&CTonyDeltaMicroSeconds(){Reset();} &&
&&&&~CTonyDeltaMicroSeconds(){} &&
&&&&void&Reset(void) &&
&&&&&&&&m_Count.Reset(_GetTimeOfDay()); &&
&&&&&&&&m_nFix=0;&&&&
&&&&&&&&m_Mean.Reset();&&&&
&&&&void&PrintInfo(void) &&
&&&&&&&&printf("m_nFix=%d\n",m_nFix); &&
&&&&&&&&m_Mean.PrintInfo(); &&
&&&&DWORD&TouchBegin(void){return&m_Count.SetBegin(_GetTimeOfDay());} &&
&&&&DWORD&TouchEnd(void){return&m_Count.SetEnd(_GetTimeOfDay());} &&
&&&&DWORD&GetBegin(void){return&m_Count.GetBegin();} &&
&&&&DWORD&GetEnd(void){return&m_Count.GetEnd();} &&
&&&&DWORD&GetDeltaMicroSeconds(void){return&m_Count.GetX();} &&
private: &&
&&&&CDWORDCount&m_C &&
&&&&CTonyMean&m_M&&&&
&&&&int&m_nF&&&&
大家注意,其实基本类很简单,都在调用前一个类,仅仅是把基数绑定在系统取得的毫秒数上。
然后,我开始考虑怎么样计时,其实基本原理很简单,我内部保存了Begin和End,两次时间点的毫秒差,就是内部的DeltaTime,这个和外部传进来的时间段MaxDelta比较一下,如果内部DeltaTime&外部时差,则表示时间到了,可以返回真,否则返回假,所以,我写了第一个计时函数TimeIsUp0。
bool&TimeIsUp0(const&DWORD&dwMaxDeltaMicroSeconds) &&
&&&&return&(dwMaxDeltaMicroSeconds&=GetDeltaMicroSeconds()); &&
简单吧,呵呵。
可是效果不好,我测了一下,其精度误差在15~16ms左右,即我要求睡眠1ms,它返回真的时候是15ms。晕哦,这咋控制精度。
没办法,我开始试图修订这个算法。
这中间肯定要修订误差,不过,这个误差方法是我以前写游戏的经验,其实就是路径跟踪算法啦。当玩家控制的主角在屏幕上跑来跑去的时候,计算机控制的敌人要追踪主角的位置,总是不断计算自己和主角之间的坐标差(自己坐标 - 主角坐标),如果这个坐标差为-,表示目前自己的坐标小了,需要增加,反之则需要减少。X、Y都可以这么计算。这个体现增加和减少的变量,叫做修订因子,它和自己的绝对坐标之和,就是新的坐标。
当然,这个修订因子还可以调整加速度,即如果每次都少,则修订因子不断+1,每次和自己坐标之和,步距就大,而每次都多,步距其实也大,但是,随着两人逐渐靠近,如果两人的,比如X坐标,很接近,要么大一点,要么小一点,则根据算法,这个修订因子就在-1,0,+1之间跳变,其实相当于X不怎么变动。这是动态的计算变化。
这段可能有点绕,大家仔细看吧,我已经很努力了,但是也没办法讲更清楚,个别朋友可以给我发信息询问。
我是这么考虑的:
1、每次判定时间到了,会得到一个真,此时,内部的DeltaTime和外部要求的时间段MaxDelta,肯定不太可能一样,有误差是正常的。
2、但是,可以考虑修订,即我内部保留一个修订因子int m_nF,这个值本来是0,但是,如果我求得一个真,此时可以计算一个差值DeltaTime-MaxDelta,这表示误差,这个误差如果&0,则表示小了,修订因子需要+1,反之-1。
3、每次进来判定,判定的是MaxDelta+m_nFix的值,即利用m_nFix修订因子来影响判定结果。最终得出较为精确的时间。
由于上述思想,我写了第二个判定函数:
bool&TimeIsUp1(const&DWORD&dwMaxDeltaMicroSeconds) &&
&&&&int&nDeltaT=(int)GetDeltaMicroSeconds();&&&&&&&&&&&&&&&&&&&&&&
&&&&int&nMaxDeltaMicroSeconds=(int)dwMaxDeltaMicroS&&&&&&&&
&&&&int&nCompNumber=nMaxDeltaMicroSeconds+m_nF&&&&&&&&&&&&&&&&&
&&&&bool&bRet=(nCompNumber&=nDeltaT);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&if(bRet) &&
&&&&&&&&&&
&&&&&&&&int&nFixX=nDeltaT-nMaxDeltaMicroS&&&&&&&&&&&&&&&&&&
&&&&&&&&if(0&nFixX)&m_nFix--;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&else&if(0&nFixX)&m_nFix++;&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&return&bR &&
不过出来一测,发现精度还是太低,有改善,我获得了7~8ms的误差精度,即我要1ms,得到7~8ms,要100ms,得到93~107ms的误差范围。
我仔细分析了一下,推测可能是直接使用DeltaTime-MaxDelta的绝对值来做判定,动作太大了,m_nFix跳变抖动得厉害,影响了精度。
那,有个简单的办法来实现这个逻辑,就是使用多次DeltaTime-MaxDelta的平均值来影响m_nFix,减小抖动。不过,这带来一个问题,我手边还没有一个合用的平均值计算模块,想了一下,我干脆写了一个。
#include&&float.h& &&
inline&bool&DoubleIsZero(double&dValue){return&(abs(dValue)&DBL_EPSILON);} &&
inline&bool&DoubleIsEqual(double&dA,double&dB){return&DoubleIsZero(dA-dB);} &&
class&CTonyMean &&
public: &&
&&&&CTonyMean(){Reset();} &&
&&&&~CTonyMean(){} &&
&&&&void&Reset(void) &&
&&&&&&&&m_dAllCount=0.0; &&
&&&&&&&&m_nTimes=0; &&
&&&&&&&&m_dMean=0.0; &&
&&&&&&&&m_nOnlyPositiveNumberFlag=0; &&
&&&&&&&&m_nOnlyNegativeNumberFlag=0; &&
&&&&double&Push(double&dValue) &&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&if(0&dValue)&m_nOnlyPositiveNumberFlag=1; &&
&&&&&&&&if(0&dValue)&m_nOnlyNegativeNumberFlag=1; &&
&&&&&&&&m_nTimes++; &&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&m_dAllCount+=dV &&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&if(OnlyOneTypeNumber()) &&
&&&&&&&&&&&&if(DoubleIsZero(m_dAllCount)) &&
&&&&&&&&&&&&&&&&ResetByOverflow(); &&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&&&
&&&&&&&&if(!m_nTimes)&ResetByOverflow(); &&
&&&&&&&&&&
&&&&&&&&m_dMean=m_dAllCount/(double)(m_nTimes); &&
&&&&&&&&return&GetMean(); &&
&&&&double&Push(int&nValue){return&Push((double)nValue);} &&
&&&&double&GetMean(void){return&m_dM} &&
&&&&void&PrintInfo(void) &&
&&&&&&&&printf("CTonyMean:&AllCount=%f,&Times=%d,&Mean=%f\n", &&
&&&&&&&&&&&&m_dAllCount,m_nTimes,m_dMean); &&
&&&&&&&&printf("Positive=%d,&Negative=%d,&OnlyOneTypeNumber()=%d\n", &&
&&&&&&&&&&&&m_nOnlyPositiveNumberFlag, &&
&&&&&&&&&&&&m_nOnlyNegativeNumberFlag, &&
&&&&&&&&&&&&OnlyOneTypeNumber()); &&
private: &&
&&&&int&OnlyOneTypeNumber(void) &&
&&&&{return&(m_nOnlyPositiveNumberFlag^m_nOnlyNegativeNumberFlag);} &&
&&&&void&ResetByOverflow(void) &&
&&&&&&&&&&
&&&&&&&&m_dAllCount=m_dM &&
&&&&&&&&&&
&&&&&&&&m_nTimes=1; &&
&&&&unsigned&int&m_nT&&&&&&&&&&&&&&&&&&&&
&&&&double&m_dAllC&&&&&&&&&&&&&&&&&&&&&&&
&&&&double&m_dM&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&unsigned&int&m_nOnlyPositiveNumberF&&&
&&&&unsigned&int&m_nOnlyNegativeNumberF&&&
inline&void&TestCTonyMean(void) &&
&&&&CTonyMean&M &&
&&&&Mean.Reset(); &&
&&&&Mean.Push(1); &&
&&&&Mean.Push(2); &&
&&&&Mean.PrintInfo(); &&
&&&&Mean.Reset(); &&
&&&&Mean.Push(-1); &&
&&&&Mean.Push(-2); &&
&&&&Mean.PrintInfo(); &&
&&&&Mean.Reset(); &&
&&&&Mean.Push(-1); &&
&&&&Mean.Push(2); &&
&&&&Mean.PrintInfo(); &&
我想我注释挺明白的,就不多说了吧。大家现在知道计时器里面的CTonyMean m_M怎么来的了吧。呵呵。
嗯,这段代码调整过,原来的老代码,只能处理纯正数或者有正负数,现在调整为可以同时处理纯正数,正负数,以及纯负数三种情况,这样适用面更广一点。嗯,我顺手加了个测试代码,大家有兴趣可以run起来看看。
然后,我写了第三段精确计时逻辑。
bool&TimeIsUp2(const&DWORD&dwMaxDeltaMicroSeconds) &&
&&&&int&nDeltaT=(int)GetDeltaMicroSeconds();&&&&&&&&&&&&&&&&&&&&&&
&&&&int&nMaxDeltaMicroSeconds=(int)dwMaxDeltaMicroS&&&&&&&&
&&&&int&nCompNumber=nMaxDeltaMicroSeconds+m_nF&&&&&&&&&&&&&&&&&
&&&&bool&bRet=(nCompNumber&=nDeltaT);&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&if(bRet) &&
&&&&&&&&m_Mean.Push(nDeltaT-nMaxDeltaMicroSeconds);&&&&&&&&&&&&&&&
&&&&&&&&if(0.0&m_Mean.GetMean())&m_nFix--;&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&else&if(0.0&m_Mean.GetMean())&m_nFix++; &&
&&&&return&bR &&
大家请注意平均值计算的代入。
这段代码效果很好,我测试了一下,获得了+-0.003ms左右的精度。
当然,我想了一下,干脆,这些个代码全部提供,所以我写了个集中的函数,利用switch选择用哪种计时精度。
bool&TimeIsUp(const&DWORD&dwMaxDeltaMicroSeconds,int&nModel=2) &&
&&&&bool&bRet=false; &&
&&&&TouchEnd(); &&
&&&&switch(nModel) &&
&&&&default:&&&&&&&&&&
&&&&case&0:&&&&&&&&&&&
&&&&&&&&bRet=TimeIsUp0(dwMaxDeltaMicroSeconds); &&
&&&&&&&&break; &&
&&&&case&1:&&&&&&&&&&&
&&&&&&&&bRet=TimeIsUp1(dwMaxDeltaMicroSeconds); &&
&&&&&&&&break; &&
&&&&case&2:&&&&&&&&&&&
&&&&&&&&bRet=TimeIsUp2(dwMaxDeltaMicroSeconds); &&
&&&&&&&&break; &&
&&&&return&bR &&
嗯,这是判定逻辑,有时候,业务层可能不想自己做个循环频繁判定,因此需要这个逻辑自己提供一个等待睡眠的函数,我也写了一个。
void&Wait4(const&DWORD&dwMaxDeltaMicroSeconds,int&nModel=2) &&
{while(!TimeIsUp(dwMaxDeltaMicroSeconds,nModel)){TonyXiaoMinSleep();}}&&
嗯,没有看过《0bug-C/C++商用工程之道》这本书的朋友,可能看不懂TonyXiaoMinSleep();这个函数,那简单,直接用Sleep(0)代替好了。
最后,我写了一段代码测试一下,由于这里面有动态跟踪逻辑,因此,前面几次测试肯定不准,需要工作一段时间,m_nFix调整得差不多了,精度就高了,因此,我写了如下逻辑。每种模式,0、1、2三种,分别测试5轮,每轮测试1000次,取平均值,来看测试的精度。
精度测试很简单,我用个计数器,每次叠加DeltaTime这个模块内部的Delta值,最后除以总的访问次数,就得到平均每次的毫秒数。
#define&TestCDeltaMicroSeconds_MAX_COUNT&1000 &&
inline&void&TestCDeltaMicroSeconds(void) &&
&&&&CTonyMean&TimeMeadPreM &&
&&&&CTonyDeltaMicroSeconds& &&
&&&&int&i=0; &&
&&&&int&j=0; &&
&&&&int&k=0; &&
&&&&int&nCount=0; &&
&&&&double&dMS=0.0; &&
&&&&for(i=0;i&3;i++) &&
&&&&&&&&TimeMeadPreModel.Reset(); &&
&&&&&&&&for(j=0;j&5;j++) &&
&&&&&&&&{ &&
&&&&&&&&&&&&printf("Test:&model=%d,&try=%d,&test&%d&times&pre&try\n",i,j,TestCDeltaMicroSeconds_MAX_COUNT); &&
&&&&&&&&&&&&nCount=0; &&
&&&&&&&&&&&&dms.Reset(); &&
&&&&&&&&&&&&for(k=0;k&TestCDeltaMicroSeconds_MAX_COUNT;k++) &&
&&&&&&&&&&&&{ &&
&&&&&&&&&&&&&&&&dms.TouchBegin(); &&
&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&dms.Wait4(1,i); &&
&&&&&&&&&&&&&&&&nCount+=dms.GetDeltaMicroSeconds(); &&
&&&&&&&&&&&&} &&
&&&&&&&&&&&&dMS=(double)nCount/(double)TestCDeltaMicroSeconds_MAX_COUNT; &&
&&&&&&&&&&&&TimeMeadPreModel.Push(dMS); &&
&&&&&&&&&&&&printf("Model&%d,&try=%d&%d&times:&Mean=&%f&ms\n",i,j,TestCDeltaMicroSeconds_MAX_COUNT,dMS); &&
&&&&&&&&&&&&printf("-----------\n"); &&
&&&&&&&&} &&
&&&&&&&&printf("Model&%d&Mean:&%f&ms\n",i,TimeMeadPreModel.GetMean()); &&
&&&&&&&&printf("=====================================\n"); &&
运行结果如下:
Test:&model=0,&try=0,&test&1000&times&pre&try&&
Model&0,&try=0&1000&times:&Mean=&15.625000&ms &&
----------- &&
Test:&model=0,&try=1,&test&1000&times&pre&try&&
Model&0,&try=1&1000&times:&Mean=&15.625000&ms &&
----------- &&
Test:&model=0,&try=2,&test&1000&times&pre&try&&
Model&0,&try=2&1000&times:&Mean=&15.625000&ms &&
----------- &&
Test:&model=0,&try=3,&test&1000&times&pre&try&&
Model&0,&try=3&1000&times:&Mean=&15.625000&ms &&
----------- &&
Test:&model=0,&try=4,&test&1000&times&pre&try&&
Model&0,&try=4&1000&times:&Mean=&15.625000&ms &&
----------- &&
Model&0&Mean:&15.625000&ms&&&&&&
===================================== &&
Test:&model=1,&try=0,&test&1000&times&pre&try&&
Model&1,&try=0&1000&times:&Mean=&7.813000&ms &&
----------- &&
Test:&model=1,&try=1,&test&1000&times&pre&try&&
Model&1,&try=1&1000&times:&Mean=&7.812000&ms &&
----------- &&
Test:&model=1,&try=2,&test&1000&times&pre&try&&
Model&1,&try=2&1000&times:&Mean=&7.813000&ms &&
----------- &&
Test:&model=1,&try=3,&test&1000&times&pre&try&&
Model&1,&try=3&1000&times:&Mean=&7.812000&ms &&
----------- &&
Test:&model=1,&try=4,&test&1000&times&pre&try&&
Model&1,&try=4&1000&times:&Mean=&7.813000&ms &&
----------- &&
Model&1&Mean:&7.812600&ms&&&&&&
===================================== &&
Test:&model=2,&try=0,&test&1000&times&pre&try&&
Model&2,&try=0&1000&times:&Mean=&1.000000&ms &&
----------- &&
Test:&model=2,&try=1,&test&1000&times&pre&try&&
Model&2,&try=1&1000&times:&Mean=&1.000000&ms &&
----------- &&
Test:&model=2,&try=2,&test&1000&times&pre&try&&
Model&2,&try=2&1000&times:&Mean=&1.000000&ms &&
----------- &&
Test:&model=2,&try=3,&test&1000&times&pre&try&&
Model&2,&try=3&1000&times:&Mean=&1.000000&ms &&
----------- &&
Test:&model=2,&try=4,&test&1000&times&pre&try&&
Model&2,&try=4&1000&times:&Mean=&1.000000&ms &&
----------- &&
Model&2&Mean:&1.000000&ms&&&&&&
=====================================&&
呵呵,当然,由于这个模块本意是要跨线程工作,我这里给出它的资源锁封装版本。
class&CTonyDeltaMicroSecondsWithLock &&
public: &&
&&&&CTonyDeltaMicroSecondsWithLock(){} &&
&&&&~CTonyDeltaMicroSecondsWithLock(){} &&
public: &&
&&&&void&Reset(void){TONY_LOCK_CALL(m_TonyDeltaMicroSeconds.Reset())} &&
&&&&void&PrintInfo(void){TONY_LOCK_CALL(m_TonyDeltaMicroSeconds.PrintInfo());} &&
&&&&DWORD&TouchBegin(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.TouchBegin());} &&
&&&&DWORD&TouchEnd(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.TouchEnd());} &&
&&&&DWORD&GetBegin(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetBegin());} &&
&&&&DWORD&GetEnd(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetEnd());} &&
&&&&DWORD&GetDeltaMicroSeconds(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetDeltaMicroSeconds());} &&
&&&&bool&TimeIsUp(const&DWORD&dwMaxDeltaMicroSeconds,int&nModel=2) &&
&&&&{TONY_BOOL_LOCK_CALL(m_TonyDeltaMicroSeconds.TimeIsUp(dwMaxDeltaMicroSeconds,nModel));} &&
private: &&
&&&&CTonyDeltaMicroSeconds&m_TonyDeltaMicroS &&
&&&&CMutexLock&m_L &&
嗯,中间用到几个宏,是我图省事,写的。0bug里面讲过没有,我忘了,嘿嘿。这里给出来。
#define&TONY_LOCK_CALL(func)&\ &&
&&&&m_Lock.Lock();&\ &&
&&&&m_Lock.Unlock();&\ &&
#define&TONY_DWORD_LOCK_CALL(func)&\ &&
&&&&DWORD&dwR&\ &&
&&&&m_Lock.Lock();&\ &&
&&&&dwRet=&\ &&
&&&&m_Lock.Unlock();&\ &&
&&&&return&dwR&\ &&
#define&TONY_BOOL_READ_LOCK_CALL(func)&\ &&
&&&&bool&bR&\ &&
&&&&m_Lock.AddRead();&\ &&
&&&&bRet=&\ &&
&&&&m_Lock.DecRead();&\ &&
&&&&return&bR&\ &&
差不多就这么多吧,内容有点多,大家慢慢看哈。呵呵。
没办法,有那么多细节要考虑,我也写了整整一个下午呢。本程序在VS2008下测试通过。不过,没有经过长时间运行,这段代码我不承诺0bug的。大家注意哈。
=======================================================在线底价购买《0bug-C/C++商用工程之道》(直接点击下面链接或拷贝到浏览器地址栏)
肖舸本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)
20:11:18 04:05:43}

我要回帖

更多关于 鲁大师跑分排行榜 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信