版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、編譯原理課程實(shí)驗(yàn)報(bào)告題目 題目 語法分析器的設(shè)計(jì)與實(shí)現(xiàn)專業(yè) 軟件工程 班級(jí) 學(xué)號(hào) 姓名 指導(dǎo)教師 哈爾濱工程大學(xué)軟件學(xué)院2015年5月實(shí)驗(yàn)2:語法分析一、實(shí)驗(yàn)?zāi)康撵柟虒?duì)語法分析的基本功能和原理的認(rèn)識(shí)。通過對(duì)語法分析表的自動(dòng)生成加深語法分析表的認(rèn)識(shí)。理解并處理語法分析中的異常和錯(cuò)誤。二、實(shí)驗(yàn)內(nèi)容本程序是基于已構(gòu)建好的某一個(gè)語法的預(yù)測(cè)分析表來對(duì)用戶的輸入字符串進(jìn)行分析,判斷輸入的字符串是否屬于該文法的句子?;緦?shí)現(xiàn)思想:接收用戶輸入的字符串(字符串以“#”表示結(jié)束)后,對(duì)用做分析棧的一維數(shù)組和存放分析表的二維數(shù)組進(jìn)行初始化。然后取出分析棧的棧頂字符,判斷是否為終結(jié)符,若為終結(jié)符則判斷是否為“#”且
2、與當(dāng)前輸入符號(hào)一樣,若是則語法分析結(jié)束,輸入的字符串為文法的一個(gè)句子,否則出錯(cuò)若不為“#”且與當(dāng)前輸入符號(hào)一樣則將棧頂符號(hào)出棧,當(dāng)前輸入符號(hào)從輸入字符串中除去,進(jìn)入下一個(gè)字符的分析。若不為“#”且不與當(dāng)前輸入符號(hào)一樣,則出錯(cuò)。若棧頂符號(hào)為非終結(jié)符時(shí),查看預(yù)測(cè)分析表,看棧頂符號(hào)和當(dāng)前輸入符號(hào)是否構(gòu)成產(chǎn)生式,若產(chǎn)生式的右部為,則將棧頂符號(hào)出棧,取出棧頂符號(hào)進(jìn)入下一個(gè)字符的分析。若不為,將產(chǎn)生式的右部逆序的入棧,取出棧頂符號(hào)進(jìn)入下一步分析。程序流程圖:本程序中使用以下文法作對(duì)用戶輸入的字符串進(jìn)行分析:ETEE+TE|TFTT*FT|Fi|(E)該文法的預(yù)測(cè)分析表為:代碼:package ;impor
3、t java.io.*;public class LL String Vn = E, E, T, T, F ; / 非終結(jié)符集String Vt = i, +, *, (, ), # ; / 終結(jié)符集String P = new String56; / 預(yù)測(cè)分析表String fenxi ; / 分析棧int count = 1; / 步驟int count1 = 1;/分析棧指針int count2 = 0, count3 = 0;/預(yù)測(cè)分析表指針String inputString = ; / 輸入的字符串boolean flag;public void setCount(int coun
4、t, int count1, int count2, int count3)this.count = count; this.count1 = count1;this.count2 = count2;this.count3 = count3;flag = false;public void setFenxi() / 初始化分析棧fenxi = new String20;fenxi0 = #;fenxi1 = E;public void setP() / 初始化預(yù)測(cè)分析表for (int i = 0; i 5; i+) for (int j = 0; j TE;P03 = -TE;P11 = -
5、+TE;P14 = -;P15 = -;P20 = -FT;P23 = -FT;P31 = -;P32 = -*FT;P34 = -;P35 = -;P40 = -i;P43 = -(E);/ 打印出預(yù)測(cè)分析表System.out.println( 已構(gòu)建好的預(yù)測(cè)分析表);System.out.println(-);for (int i=0; i6; i+) System.out.print( +Vti);System.out.println();System.out.println(-);for (int i=0; i5; i+) System.out.print( +Vni+ );for
6、(int j=0; j0) l = 10-Pij-1.length();for (int k=0; k= 0) for (int i=0; i6; i+) if (fenxicount1.equals(Vti) / 判斷分析棧棧頂?shù)淖址欠駷榻K結(jié)符flage = true;break;if (flage) / 為終結(jié)符時(shí)if (fenxicount1.equals(inputChar) if (fenxicount1.equals(#)&inputString.length()=1) / 棧頂符號(hào)為結(jié)束標(biāo)志時(shí)/ System.out.println(最后一個(gè));String fenxizhan
7、 = ;for (int i=0; i=P.length; i+) / 拿到分析棧里的全部內(nèi)容(濾去null)if (fenxii = null) break; else fenxizhan = fenxizhan + fenxii;/ 輸出當(dāng)前分析棧情況,輸入字符串,所用產(chǎn)生式或匹配System.out.print( + count);String countToString = Integer.toString(count);int farWay = 14 - countToString.length();for (int k=0; kfarWay; k+) System.out.prin
8、t( );System.out.print(fenxizhan);farWay = 20 - fenxizhan.length();for (int k=0; kfarWay; k+) System.out.print( );System.out.print(inputString);farWay = 25 - inputString.length();for (int k=0; kfarWay; k+) System.out.print( );System.out.println(接受);flag = true;return true; else / 分析棧棧頂符號(hào)不為結(jié)束標(biāo)志符號(hào)時(shí)Stri
9、ng fenxizhan = ;for (int i=0; i=P.length; i+) / 拿到分析棧里的全部內(nèi)容(濾去null)if (fenxii = null) break; else fenxizhan = fenxizhan + fenxii;/ 輸出當(dāng)前分析棧情況,輸入字符串,所用產(chǎn)生式或匹配System.out.print( +count);String countToString = Integer.toString(count);int farWay = 14 - countToString.length();for (int k=0; kfarWay; k+) Syst
10、em.out.print( );System.out.print(fenxizhan);farWay = 20 - fenxizhan.length();for (int k=0; kfarWay; k+) System.out.print( );System.out.print(inputString);farWay = 25 - inputString.length();for (int k=0; k 1) / 當(dāng)當(dāng)前輸入字符串的長度大于1時(shí),將當(dāng)前輸入字符從輸入字符串中除去inputString = inputString.substring(1, inputString.length(
11、); else / 當(dāng)前輸入串長度為1時(shí)inputChar = inputString;/ System.out.println( +count+ +fenxizhan+/ +inputString + +Pcount3count2);/ System.out.println(count + inputChar + 匹配 );count+;judge();else / 判斷與與輸入符號(hào)是否一樣為結(jié)束標(biāo)志System.out.println( 分析到第 + count + 步時(shí)出錯(cuò)!);flag = false;return false; else / 非終結(jié)符時(shí)boolean fla = fa
12、lse; for (int i=0; i6; i+) / 查詢當(dāng)前輸入符號(hào)位于終結(jié)符集的位置if (inputChar.equals(Vti) fla = true;count2 = i;break;if(!fla)System.out.println( 分析到第 + count + 步時(shí)出錯(cuò)!);flag = false;return false;for (int i=0; i5; i+) / 查詢棧頂?shù)姆?hào)位于非終結(jié)符集的位置if (fenxicount1.equals(Vni) count3 = i;break;if (Pcount3count2 != error) / 棧頂?shù)姆墙K結(jié)符與
13、輸入的終結(jié)符存在產(chǎn)生式時(shí)String p = Pcount3count2;String s1 = p.substring(2, p.length(); / 獲取對(duì)應(yīng)的產(chǎn)生式if (s1.equals() / 產(chǎn)生式推出“”時(shí)String fenxizhan = ;for (int i=0; i=P.length; i+) if (fenxii = null) break; else fenxizhan = fenxizhan + fenxii;/ 輸出當(dāng)前分析棧情況,輸入字符串,所用產(chǎn)生式或匹配System.out.print( + count);String countToString =
14、Integer.toString(count);int farWay = 14 - countToString.length();for (int k=0; kfarWay; k+) System.out.print( );System.out.print(fenxizhan);farWay = 20 - fenxizhan.length();for (int k=0; kfarWay; k+) System.out.print( );System.out.print(inputString);farWay = 25 - inputString.length();for (int k=0; k
15、farWay; k+) System.out.print( );System.out.println(fenxicount1 + Pcount3count2);/ 將棧頂符號(hào)出棧,棧頂指針指向下一個(gè)元素fenxicount1 = null;count1 -= 1;count+;judge(); else / 產(chǎn)生式不推出“”時(shí)int k = s1.length();String fenxizhan = ;for (int i=0; i=P.length; i+) if (fenxii = null) break; else fenxizhan = fenxizhan + fenxii;/ 輸出
16、當(dāng)前分析棧情況,輸入字符串,所用產(chǎn)生式或匹配System.out.print( +count);String countToString = Integer.toString(count);int farWay = 14 - countToString.length();for (int o=0; ofarWay; o+) System.out.print( );System.out.print(fenxizhan);farWay = 20 - fenxizhan.length();for (int o=0; ofarWay; o+) System.out.print( );System.ou
17、t.print(inputString);farWay = 25 - inputString.length();for (int o=0; ofarWay; o+) System.out.print( );System.out.println(fenxicount1 + Pcount3count2);for (int i=1; i=k; i+) / 將產(chǎn)生式右部的各個(gè)符號(hào)入棧String s2 = s1.substring(s1.length() - 1, s1.length();s1 = s1.substring(0, s1.length() - 1);if (s2.equals() s2
18、= s1.substring(s1.length() - 1, s1.length()+ s2;i+;s1 = s1.substring(0, s1.length() - 1);fenxicount1 = s2;if (i k)count1+;/ System.out.println(count1= + count1);/ System.out.println( +count+ +fenxizhan+/ +inputString + +Pcount3count2);count+;/ System.out.println(count);judge(); else System.out.print
19、ln( 分析到第 + count + 步時(shí)出錯(cuò)!);flag = false;return false;return flag;public static void main(String args) LL l = new LL();l.setP();String input = ;boolean flag = true;while (flag) try InputStreamReader isr = new InputStreamReader(System.in);BufferedReader br = new BufferedReader(isr);System.out.println()
20、;System.out.print(請(qǐng)輸入字符串(輸入exit退出):);input = br.readLine(); catch (Exception e) e.printStackTrace();if(input.equals(exit)flag = false;elsel.setInputString(input);l.setCount(1, 1, 0, 0);l.setFenxi();System.out.println();System.out.println(分析過程);System.out.println(-);System.out.println( 步驟 | 分析棧 | 剩余輸
21、入串 | 所用產(chǎn)生式 );System.out.println(-);boolean b = l.judge();System.out.println(-);if(b)System.out.println(您輸入的字符串+input+是該文發(fā)的一個(gè)句子);elseSystem.out.println(您輸入的字符串+input+有詞法錯(cuò)誤!);三、實(shí)驗(yàn)結(jié)果1、顯示預(yù)測(cè)分析表,提示用戶輸入字符串2、輸入的字符串為正確的句子3、輸入的字符串中包含了不屬于終結(jié)符集的字符4、輸入的字符串不是該文法能推導(dǎo)出來的句子四、實(shí)驗(yàn)中遇到的問題總結(jié)主要闡述兩方面的問題實(shí)驗(yàn)過程中遇到的問題如何解決的?實(shí)驗(yàn)中遇到的問
22、題,通過查書、和上網(wǎng)搜索資料解決問題。(二)思考題的思考與分析思考題1:給出在生成語法分析表時(shí)所遇到的困難,以及是如何處理的?生成分析表時(shí),需要用到棧,有很多難處,但是通過網(wǎng)絡(luò),逐漸還是解決了問題。思考題2:思考還可以什么形式來給出語法分析的結(jié)果?若分析結(jié)果符合該語法,還可以用語法分析樹的形式表示。思考題3:如果在語法分析中遇到了語法錯(cuò)誤,是應(yīng)該中斷語法分析呢,還是應(yīng)該進(jìn)行適當(dāng)處理后繼續(xù)語法分析,你是怎么處理的?語法分析不同于詞法分析,一旦語法分析錯(cuò)了一個(gè)產(chǎn)生式,那么很可能導(dǎo)致后面的產(chǎn)生式全部出錯(cuò)。所以我選擇一旦出現(xiàn)語法錯(cuò)誤即中斷分析并報(bào)錯(cuò)。五、實(shí)驗(yàn)體會(huì)包括收獲、心得體會(huì)、存在的問題及解決問題
23、的方法、建議等通過進(jìn)行本次語法分析實(shí)驗(yàn),我在實(shí)踐中加深了對(duì)于語法分析基本內(nèi)容和原理的了解和認(rèn)識(shí),以及對(duì)語法分析表有了更深一層的了解。在本次實(shí)驗(yàn)過程中,難度對(duì)于同學(xué)們來說的確很大,希望該實(shí)驗(yàn)老師以后能夠給給更多的指導(dǎo),讓同學(xué)們明白應(yīng)該做什么,應(yīng)該怎么做。附錄資料:不需要的可以自行刪除C語言中如何獲取時(shí)間?精度如何?1 使用time_t time( time_t * timer ) 精確到秒2 使用clock_t clock() 得到的是CPU時(shí)間精確到1/CLOCKS_PER_SEC秒3 計(jì)算時(shí)間差使用double difftime( time_t timer1, time_t timer0 )
24、4 使用DWORD GetTickCount() 精確到毫秒5 如果使用MFC的CTime類,可以用CTime:GetCurrentTime() 精確到秒6 要獲取高精度時(shí)間,可以使用BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency)獲取系統(tǒng)的計(jì)數(shù)器的頻率BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount)獲取計(jì)數(shù)器的值然后用兩次計(jì)數(shù)器的差除以Frequency就得到時(shí)間。7 Multimedia Timer FunctionsThe following
25、functions are used with multimedia timers.timeBeginPeriod/timeEndPeriod/timeGetDevCaps/timeGetSystemTime/*/用標(biāo)準(zhǔn)C實(shí)現(xiàn)獲取當(dāng)前系統(tǒng)時(shí)間的函數(shù)一.time()函數(shù)time(&rawtime)函數(shù)獲取當(dāng)前時(shí)間距1970年1月1日的秒數(shù),以秒計(jì)數(shù)單位,存于rawtime 中。#include time.hvoid main ()time_t rawtime;struct tm * timeinfo;time ( &rawtime );timeinfo = localtime ( &rawtim
26、e );printf ( 007The current date/time is: %s, asctime (timeinfo) );exit(0);=#include - 必須的時(shí)間函數(shù)頭文件time_t - 時(shí)間類型(time.h 定義是typedef long time_t; 追根溯源,time_t是long)struct tm - 時(shí)間結(jié)構(gòu),time.h 定義如下:int tm_sec;int tm_min;int tm_hour;int tm_mday;int tm_mon;int tm_year;int tm_wday;int tm_yday;int tm_isdst;time (
27、 &rawtime ); - 獲取時(shí)間,以秒計(jì),從1970年1月一日起算,存于rawtimelocaltime ( &rawtime ); - 轉(zhuǎn)為當(dāng)?shù)貢r(shí)間,tm 時(shí)間結(jié)構(gòu)asctime ()- 轉(zhuǎn)為標(biāo)準(zhǔn)ASCII時(shí)間格式:星期 月 日 時(shí):分:秒 年-二.clock()函數(shù),用clock()函數(shù),得到系統(tǒng)啟動(dòng)以后的毫秒級(jí)時(shí)間,然后除以CLOCKS_PER_SEC,就可以換成“秒”,標(biāo)準(zhǔn)c函數(shù)。clock_t clock ( void );#includeclock_t t = clock();long sec = t / CLOCKS_PER_SEC;他是記錄時(shí)鐘周期的,實(shí)現(xiàn)看來不會(huì)很精確,
28、需要試驗(yàn)驗(yàn)證;-三.gettime(&t); 據(jù)說tc2.0的time結(jié)構(gòu)含有毫秒信息#include#includeint main(void)struct time t;gettime(&t);printf(The current time is: -:d:d.dn,t.ti_hour, t.ti_min, t.ti_sec, t.ti_hund);return 0;time 是一個(gè)結(jié)構(gòu)體, 其中成員函數(shù) ti_hund 是毫秒。-四.GetTickCount(),這個(gè)是windows里面常用來計(jì)算程序運(yùn)行時(shí)間的函數(shù);DWORD dwStart = GetTickCount();/這里運(yùn)行
29、你的程序代碼DWORD dwEnd = GetTickCount();則(dwEnd-dwStart)就是你的程序運(yùn)行時(shí)間, 以毫秒為單位這個(gè)函數(shù)只精確到55ms,1個(gè)tick就是55ms。-五.timeGetTime()t,imeGetTime()基本等于GetTickCount(),但是精度更高DWORD dwStart = timeGetTime();/這里運(yùn)行你的程序代碼DWORD dwEnd = timeGetTime();則(dwEnd-dwStart)就是你的程序運(yùn)行時(shí)間, 以毫秒為單位雖然返回的值單位應(yīng)該是ms,但傳說精度只有10ms。=/*Unix#unix時(shí)間相關(guān),也是標(biāo)準(zhǔn)
30、庫的/*1.timegm函數(shù)只是將struct tm結(jié)構(gòu)轉(zhuǎn)成time_t結(jié)構(gòu),不使用時(shí)區(qū)信息;time_t timegm(struct tm *tm);2.mktime使用時(shí)區(qū)信息time_t mktime(struct tm *tm);timelocal 函數(shù)是GNU擴(kuò)展的與posix函數(shù)mktime相當(dāng)time_t timelocal (struct tm *tm);3.gmtime函數(shù)只是將time_t結(jié)構(gòu)轉(zhuǎn)成struct tm結(jié)構(gòu),不使用時(shí)區(qū)信息;struct tm * gmtime(const time_t *clock);4.localtime使用時(shí)區(qū)信息struct tm * l
31、ocaltime(const time_t *clock);1.time獲取時(shí)間,stime設(shè)置時(shí)間time_t t;t = time(&t);2.stime其參數(shù)應(yīng)該是GMT時(shí)間,根據(jù)本地時(shí)區(qū)設(shè)置為本地時(shí)間;int stime(time_t *tp)3.UTC=true 表示采用夏時(shí)制;4.文件的修改時(shí)間等信息全部采用GMT時(shí)間存放,不同的系統(tǒng)在得到修改時(shí)間后通過localtime轉(zhuǎn)換成本地時(shí)間;5.設(shè)置時(shí)區(qū)推薦使用setup來設(shè)置;6.設(shè)置時(shí)區(qū)也可以先更變/etc/sysconfig/clock中的設(shè)置再將ln -fs /usr/share/zoneinfo/xxxx/xxx /etc/l
32、ocaltime 才能重效time_t只能表示68年的范圍,即mktime只能返回1970-2038這一段范圍的time_t看看你的系統(tǒng)是否有time_t64,它能表示更大的時(shí)間范圍/*windows#Window里面的一些不一樣的/*一.CTime () 類VC編程一般使用CTime類 獲得當(dāng)前日期和時(shí)間CTime t = GetCurrentTime();SYSTEMTIME 結(jié)構(gòu)包含毫秒信息typedef struct _SYSTEMTIME WORD wYear;WORD wMonth;WORD wDayOfWeek;WORD wDay;WORD wHour;WORD wMinute;
33、WORD wSecond;WORD wMilliseconds; SYSTEMTIME, *PSYSTEMTIME;SYSTEMTIME t1;GetSystemTime(&t1)CTime curTime(t1);WORD ms = t1.wMilliseconds;SYSTEMTIME sysTm;:GetLocalTime(&sysTm);在time.h中的_strtime() /只能在windows中用char t11;_strtime(t);puts(t);/*獲得當(dāng)前日期和時(shí)間CTime tm=CTime:GetCurrentTime();CString str=tm.Format
34、(%Y-%m-%d);在VC中,我們可以借助CTime時(shí)間類,獲取系統(tǒng)當(dāng)前日期,具體使用方法如下:CTime t = CTime:GetCurrentTime(); /獲取系統(tǒng)日期,存儲(chǔ)在t里面int d=t.GetDay(); /獲得當(dāng)前日期int y=t.GetYear(); /獲取當(dāng)前年份int m=t.GetMonth(); /獲取當(dāng)前月份int h=t.GetHour(); /獲取當(dāng)前為幾時(shí)int mm=t.GetMinute(); /獲取當(dāng)前分鐘int s=t.GetSecond(); /獲取當(dāng)前秒int w=t.GetDayOfWeek(); /獲取星期幾,注意1為星期天,7為星
35、期六二.CTimeSpan類如果想計(jì)算兩段時(shí)間的差值,可以使用CTimeSpan類,具體使用方法如下:CTime t1( 1999, 3, 19, 22, 15, 0 );CTime t = CTime:GetCurrentTime();CTimeSpan span=t-t1; /計(jì)算當(dāng)前系統(tǒng)時(shí)間與時(shí)間t1的間隔int iDay=span.GetDays(); /獲取這段時(shí)間間隔共有多少天int iHour=span.GetTotalHours(); /獲取總共有多少小時(shí)int iMin=span.GetTotalMinutes();/獲取總共有多少分鐘int iSec=span.GetTot
36、alSeconds();/獲取總共有多少秒-三._timeb()函數(shù)_timeb定義在SYSTIMEB.H,有四個(gè)fieldsdstflagmillitmtimetimezonevoid _ftime( struct _timeb *timeptr );struct _timeb timebuffer;_ftime( &timebuffer );取當(dāng)前時(shí)間:文檔講可以到ms,有人測(cè)試,好象只能到16ms!四.設(shè)置計(jì)時(shí)器定義TIMER ID#define TIMERID_JISUANFANGSHI 2在適當(dāng)?shù)牡胤皆O(shè)置時(shí)鐘,需要開始其作用的地方;SetTimer(TIMERID_JISUANFAN
37、GSHI,200,NULL);在不需要定時(shí)器的時(shí)候的時(shí)候銷毀掉時(shí)鐘KillTimer(TIMERID_JISUANFANGSHI);對(duì)應(yīng)VC程序的消息映射void CJisuan:OnTimer(UINT nIDEvent)switch(nIDEvent)-#如何設(shè)定當(dāng)前系統(tǒng)時(shí)間-windowsSYSTEMTIME m_myLocalTime,*lpSystemTime;m_myLocalTime.wYear=2003;m_myLocalTime.wM;m_myLocalTime.wDay=1;m_myLocalTime.wHour=0;m_myLocalTime.wMinute=0;m_my
38、LocalTime.wSec;m_myLocalTime.wMillisec;lpSystemTime=&m_myLocalTime;if( SetLocalTime(lpSystemTime) ) /此處換成 SetSystemTime( )也不行MessageBox(OK !);elseMessageBox(Error !);SYSTEMTIME m_myLocalTime,*lpSystemTime;m_myLocalTime.wYear=2003;m_myLocalTime.wM;m_myLocalTime.wDay=1;lpSystemTime=&m_myLocalTime;if(
39、SetDate(lpSystemTime) ) /此處換成 SetSystemTime( )也不行MessageBox(OK !);elseMessageBox(Error !);本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:HYPERLINK /khuang2008/archive/2008/12/09/3483274.aspx/khuang2008/archive/2008/12/09/3483274.aspx一種制作微秒級(jí)精度定時(shí)器的方法當(dāng)使用定時(shí)器時(shí),在很多情況下只用到毫秒級(jí)的時(shí)間間隔,所以只需用到下面的兩種常用方式就滿足要求了。一是用SetTimer函數(shù)建立一個(gè)定時(shí)器后,在程序中通過處理由定
40、時(shí)器發(fā)送到線程消息隊(duì)列中的WM_TIMER消息,而得到定時(shí)的效果(退出程序時(shí)別忘了調(diào)用和SetTimer配對(duì)使用的KillTimer函數(shù))。二是利用GetTickCount函數(shù)可以返回自計(jì)算機(jī)啟動(dòng)后的時(shí)間,通過兩次調(diào)用GetTickCount函數(shù),然后控制它們的差值來取得定時(shí)效果,此方式跟第一種方式一樣,精度也是毫秒級(jí)的。用這兩種方式取得的定時(shí)效果雖然在許多場(chǎng)合已經(jīng)滿足實(shí)際的要求,但由于它們的精度只有毫秒級(jí)的,而且在要求定時(shí)時(shí)間間隔小時(shí),實(shí)際定時(shí)誤差大。下面介紹一種能取得高精度定時(shí)的方法。在一些計(jì)算機(jī)硬件系統(tǒng)中,包含有高精度運(yùn)行計(jì)數(shù)器(high-resolution performance c
41、ounter),利用它可以獲得高精度定時(shí)間隔,其精度與CPU的時(shí)鐘頻率有關(guān)。采用這種方法的步驟如下:1、首先調(diào)用QueryPerformanceFrequency函數(shù)取得高精度運(yùn)行計(jì)數(shù)器的頻率f。單位是每秒多少次(n/s),此數(shù)一般很大。2、在需要定時(shí)的代碼的兩端分別調(diào)用QueryPerformanceCounter以取得高精度運(yùn)行計(jì)數(shù)器的數(shù)值n1,n2。兩次數(shù)值的差值通過f換算成時(shí)間間隔,t=(n2-n1)/f。下面舉一個(gè)例子來演示這種方法的使用及它的精確度。在VC 6.0 下用MFC建立一個(gè)對(duì)話框工程,取名為HightTimer.在對(duì)話框面板中控件的布局如下圖:其中包含兩個(gè)靜態(tài)文本框,兩個(gè)
42、編輯框和兩個(gè)按紐。上面和下面位置的編輯框的ID分別為IDC_E_TEST和IDC_E_ACTUAL,通過MFC ClassWizard添加的成員變量也分別對(duì)應(yīng)為DWORD m_dwTest和DWORD m_dwAct. “退出”按紐的ID為IDOK,“開始測(cè)試”按紐ID為IDC_B_TEST,用MFC ClassWizard添加此按紐的單擊消息處理函數(shù)如下:void CHightTimerDlg:OnBTest()/ TODO: Add your control notification handler code hereUpdateData(TRUE); /取輸入的測(cè)試時(shí)間值到與編輯框相關(guān)聯(lián)
43、的成員變量m_dwTest中LARGE_INTEGER frequence;if(!QueryPerformanceFrequency( &frequence) /取高精度運(yùn)行計(jì)數(shù)器的頻率,若硬件不支持則返回FALSEMessageBox(Your computer hardware doesnt support the high-resolution performance counter,Not Support, MB_ICONEXCLAMATION | MB_OK);LARGE_INTEGER test, ret;test.QuadPart = frequence.QuadPart *
44、m_dwTest / 1000000; /通過頻率換算微秒數(shù)到對(duì)應(yīng)的數(shù)量(與CPU時(shí)鐘有關(guān)),1秒=1000000微秒ret = MySleep( test ); /調(diào)用此函數(shù)開始延時(shí),返回實(shí)際花銷的數(shù)量m_dwAct = (DWORD)(1000000 * ret.QuadPart / frequence.QuadPart ); /換算到微秒數(shù)UpdateData(FALSE); /顯示到對(duì)話框面板其中上面調(diào)用的MySleep函數(shù)如下:LARGE_INTEGER CHightTimerDlg:MySleep(LARGE_INTEGER Interval)/ 功能:執(zhí)行實(shí)際的延時(shí)功能 / 參數(shù)
45、:Interval 參數(shù)為需要執(zhí)行的延時(shí)與時(shí)間有關(guān)的數(shù)量 / 返回值:返回此函數(shù)執(zhí)行后實(shí)際所用的時(shí)間有關(guān)的數(shù)量 / LARGE_INTEGER privious, current, Elapse;QueryPerformanceCounter( &privious );current = privious;while( current.QuadPart - privious.QuadPart Interval.QuadPart )QueryPerformanceCounter( t );Elapse.QuadPart = current.QuadPart - privious.QuadPart
46、;return Elapse;注:別忘了在頭文件中為此函數(shù)添加函數(shù)聲明。至此,可以編譯和執(zhí)行此工程了,結(jié)果如上圖所示。在本人所用的機(jī)上(奔騰366, 64M內(nèi)存)測(cè)試,當(dāng)測(cè)試時(shí)間超過3微秒時(shí),準(zhǔn)確度已經(jīng)非常高了,此時(shí)機(jī)器執(zhí)行本身延時(shí)函數(shù)代碼的時(shí)間對(duì)需要延時(shí)的時(shí)間影響很小了。上面的函數(shù)由于演示測(cè)試的需要,沒有在函數(shù)級(jí)封裝,下面給出的函數(shù)基本上可以以全局函數(shù)的形式照搬到別的程序中。BOOL MySleep(DWORD dwInterval)/ 功能:執(zhí)行微秒級(jí)的延時(shí)功能 / 參數(shù):Interval 參數(shù)為需要的延時(shí)數(shù)(單位:微秒) / 返回值:若計(jì)算機(jī)硬件不支持此功能,返回FALSE,若函數(shù)執(zhí)行成
47、功,返回TRUE / BOOL bNormal = TRUE;LARGE_INTEGER frequence, privious, current, interval;if(!QueryPerformanceFrequency( &frequence):MessageBox(NULL, Your computer hardware doesnt support the high-resolution performance counter,Not Support, MB_ICONEXCLAMATION | MB_OK); /或其它的提示信息return FALSE;interval.QuadP
48、art = frequence.QuadPart * dwInterval / 1000000;bNormal = bNormal & QueryPerformanceCounter( &privious );current = privious;while( current.QuadPart - privious.QuadPart interval.QuadPart )bNormal = bNormal & QueryPerformanceCounter( t );return bNormal;需要指出的是,由于在此函數(shù)中的代碼很多,機(jī)器在執(zhí)行這些代碼所花費(fèi)的時(shí)間也很長,所以在需要幾個(gè)微秒的
49、延時(shí)時(shí),會(huì)影響精度。實(shí)際上,讀者在熟悉這種方法后,只要使用QueryPerformanceFrequency和QueryPerformanceCounter這兩個(gè)函數(shù)就能按實(shí)際需要寫出自己的延時(shí)代碼了。使用CPU時(shí)間戳進(jìn)行高精度計(jì)時(shí)對(duì)關(guān)注性能的程序開發(fā)人員而言,一個(gè)好的計(jì)時(shí)部件既是益友,也是良師。計(jì)時(shí)器既可以作為程序組件幫助程序員精確的控制程序進(jìn)程,又是一件有力的調(diào)試武器,在有經(jīng)驗(yàn)的程序員手里可以盡快的確定程序的性能瓶頸,或者對(duì)不同的算法作出有說服力的性能比較。在Windows平臺(tái)下,常用的計(jì)時(shí)器有兩種,一種是timeGetTime多媒體計(jì)時(shí)器,它可以提供毫秒級(jí)的計(jì)時(shí)。但這個(gè)精度對(duì)很多應(yīng)用場(chǎng)合
50、而言還是太粗糙了。另一種是QueryPerformanceCount計(jì)數(shù)器,隨系統(tǒng)的不同可以提供微秒級(jí)的計(jì)數(shù)。對(duì)于實(shí)時(shí)圖形處理、多媒體數(shù)據(jù)流處理、或者實(shí)時(shí)系統(tǒng)構(gòu)造的程序員,善用QueryPerformanceCount/QueryPerformanceFrequency是一項(xiàng)基本功。本文要介紹的,是另一種直接利用Pentium CPU內(nèi)部時(shí)間戳進(jìn)行計(jì)時(shí)的高精度計(jì)時(shí)手段。以下討論主要得益于Windows圖形編程一書,第15頁17頁,有興趣的讀者可以直接參考該書。關(guān)于RDTSC指令的詳細(xì)討論,可以參考Intel產(chǎn)品手冊(cè)。本文僅僅作拋磚之用。在Intel Pentium以上級(jí)別的CPU中,有一個(gè)稱為
51、“時(shí)間戳(Time Stamp)”的部件,它以64位無符號(hào)整型數(shù)的格式,記錄了自CPU上電以來所經(jīng)過的時(shí)鐘周期數(shù)。由于目前的CPU主頻都非常高,因此這個(gè)部件可以達(dá)到納秒級(jí)的計(jì)時(shí)精度。這個(gè)精確性是上述兩種方法所無法比擬的。在Pentium以上的CPU中,提供了一條機(jī)器指令RDTSC(Read Time Stamp Counter)來讀取這個(gè)時(shí)間戳的數(shù)字,并將其保存在EDX:EAX寄存器對(duì)中。由于EDX:EAX寄存器對(duì)恰好是Win32平臺(tái)下C+語言保存函數(shù)返回值的寄存器,所以我們可以把這條指令看成是一個(gè)普通的函數(shù)調(diào)用。像這樣:inline unsigned _int64 GetCycleCount
52、() _asm RDTSC 但是不行,因?yàn)镽DTSC不被C+的內(nèi)嵌匯編器直接支持,所以我們要用_emit偽指令直接嵌入該指令的機(jī)器碼形式0X0F、0X31,如下:inline unsigned _int64 GetCycleCount() _asm _emit 0 x0F _asm _emit 0 x31 以后在需要計(jì)數(shù)器的場(chǎng)合,可以像使用普通的Win32 API一樣,調(diào)用兩次GetCycleCount函數(shù),比較兩個(gè)返回值的差,像這樣: unsigned long t; t = (unsigned long)GetCycleCount(); /Do Something time-intensi
53、ve . t -= (unsigned long)GetCycleCount(); Windows圖形編程第15頁編寫了一個(gè)類,把這個(gè)計(jì)數(shù)器封裝起來。有興趣的讀者可以去參考那個(gè)類的代碼。作者為了更精確的定時(shí),做了一點(diǎn)小小的改進(jìn),把執(zhí)行RDTSC指令的時(shí)間,通過連續(xù)兩次調(diào)用GetCycleCount函數(shù)計(jì)算出來并保存了起來,以后每次計(jì)時(shí)結(jié)束后,都從實(shí)際得到的計(jì)數(shù)中減掉這一小段時(shí)間,以得到更準(zhǔn)確的計(jì)時(shí)數(shù)字。但我個(gè)人覺得這一點(diǎn)點(diǎn)改進(jìn)意義不大。在我的機(jī)器上實(shí)測(cè),這條指令大概花掉了幾十到100多個(gè)周期,在Celeron 800MHz的機(jī)器上,這不過是十分之一微秒的時(shí)間。對(duì)大多數(shù)應(yīng)用來說,這點(diǎn)時(shí)間完全可以
54、忽略不計(jì);而對(duì)那些確實(shí)要精確到納秒數(shù)量級(jí)的應(yīng)用來說,這個(gè)補(bǔ)償也過于粗糙了。 這個(gè)方法的優(yōu)點(diǎn)是: 1.高精度??梢灾苯舆_(dá)到納秒級(jí)的計(jì)時(shí)精度(在1GHz的CPU上每個(gè)時(shí)鐘周期就是一納秒),這是其他計(jì)時(shí)方法所難以企及的。 2.成本低。timeGetTime 函數(shù)需要鏈接多媒體庫winmm.lib,QueryPerformance* 函數(shù)根據(jù)MSDN的說明,需要硬件的支持(雖然我還沒有見過不支持的機(jī)器)和KERNEL庫的支持,所以二者都只能在Windows平臺(tái)下使用(關(guān)于DOS平臺(tái)下的高精度計(jì)時(shí)問題,可以參考圖形程序開發(fā)人員指南,里面有關(guān)于控制定時(shí)器8253的詳細(xì)說明)。但RDTSC指令是一條CPU指令,凡是i386平臺(tái)下Pentium以上的機(jī)器均支持,甚至沒有平臺(tái)的限制(我相信i386版本UNIX和Linux下這個(gè)方法同樣適用,但沒有條件試驗(yàn)),而且函數(shù)調(diào)用的開銷是最小的。 3.具有和CPU主頻直接對(duì)應(yīng)的速率關(guān)系。一個(gè)計(jì)數(shù)相當(dāng)于1/(CPU主頻Hz數(shù))秒,這樣只要知道了CPU的主頻,可以直接計(jì)算出
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 威視行業(yè)測(cè)繪培訓(xùn)
- 《淘寶營銷活動(dòng)技巧》課件
- 《房地產(chǎn)人職業(yè)規(guī)劃》課件
- 《課件視覺設(shè)計(jì)》課件
- 2024年四川省德陽市公開招聘警務(wù)輔助人員輔警筆試自考題1卷含答案
- 2023年陜西省漢中市公開招聘警務(wù)輔助人員輔警筆試自考題1卷含答案
- 2024年甘肅省平?jīng)鍪泄_招聘警務(wù)輔助人員輔警筆試自考題2卷含答案
- 2021年黑龍江省鶴崗市公開招聘警務(wù)輔助人員輔警筆試自考題2卷含答案
- 2021年遼寧省沈陽市公開招聘警務(wù)輔助人員輔警筆試自考題1卷含答案
- 2022年黑龍江省大興安嶺地區(qū)公開招聘警務(wù)輔助人員輔警筆試自考題2卷含答案
- 抗菌藥物的合理應(yīng)用培訓(xùn)
- 初三數(shù)學(xué)老師家長會(huì)發(fā)言稿
- 湖北第二師范學(xué)院《操作系統(tǒng)》2023-2024學(xué)年期末試卷
- 2021-2022學(xué)年河北省唐山市高一上學(xué)期期末語文試題
- 舒適化醫(yī)療麻醉
- 南寧二中、柳州高中2025屆高一上數(shù)學(xué)期末聯(lián)考試題含解析
- 吃動(dòng)平衡健康體重 課件 2024-2025學(xué)年人教版(2024)初中體育與健康七年級(jí)全一冊(cè)
- 高效能人士的七個(gè)習(xí)慣(課件)
- 2024年秋季學(xué)期新魯教版(54制)6年級(jí)上冊(cè)英語課件 Unit6 Section A (3a-3c)(第3課時(shí))
- 福建省泉州市2023-2024學(xué)年高一上學(xué)期1月教學(xué)質(zhì)量檢測(cè)(期末考試)地理試題 附答案
- 2024年廣東石油化工學(xué)院公開招聘部分新機(jī)制合同工20名歷年高頻難、易錯(cuò)點(diǎn)500題模擬試題附帶答案詳解
評(píng)論
0/150
提交評(píng)論