存儲器層次結構實驗報告
一.存儲器山
1. 實驗要求和目的
實驗要求:在自己的電腦上實驗存儲器山的實驗,並繪制如同課本445頁6-41圖片的三維圖。
實驗目的:測試自己電腦CPU的性能,並繪制出存儲器山的圖片,能夠根據實驗結果進行分析總結。
2. 實驗測試條件說明
實驗是在本人自己電腦上進行的測試,筆記本的相關信息如下:
型號: 宏碁 Acer Aspire VX5-591G
內存大小: 8G
CPU: Intel® Core™ i5-7300HQ CPU @2.50GHz 4核
高速緩存信息: L1 緩存:256KB
L2 緩存:1.0MB
L3 緩存:6.0MB
操作系統: Elementary OS
由於該實驗的測試代碼需要使用到fcyc2等頭文件,這些文件內部調用了只能在Linux系統下運行的相關函數,於是該試驗測試數據時我們是在Linux系統下進行的。
3. 實驗過程
因為實驗是在Linux系統下運行的,所以直接采用的命令行編譯了程序,然后將結果輸出到一個txt文檔中,最后直接使用MATLAB讀取txt文檔中的數據進行畫圖。整個過程由於沒有什么特別需要講解的,故在此不多敘述。
4. 實驗結果
實驗結果是分為兩部分,一部分是輸出的txt文檔,一部分是繪制的三維圖,結果如下:
實驗數據大小從1KB到128MB,步長從s1到s31。可能由於格式問題數據比較雜亂,下面是根據數據繪制出的存儲器山圖片:

5. 實驗分析
首先我們來觀察得到的實驗數據,可以看到當步長相同時,size大小從1KB到128MB變化得到的吞吐量有一個先增大再減小的過程,其中最大值出現在接近一級緩存256KB處。而當size大小固定時,步長越長,得到的吞吐量是依次減少的。
在這里我們可以探討兩個問題:第一個是為什么數據是呈現這樣的分布態勢,其中有什么原因?第二個是我們能夠從這個實驗中學習到什么東西?
首先關於數據分布態勢,也分為兩個部分。首先,為什么僅僅觀察size大小時,吞吐量最大值會出現在接近一級緩存的256KB處(實際應該更接近128KB,因為一級緩存的數據部分和指令部分是同樣的大小,理論上應該更接近數據部分的128KB)?這個其實很好解釋,從我們課上學過的存儲器結構我們可以得知從寄存器,L1到最后主存,本地磁盤訪問的速度是越來越慢的。而吞吐量計算公式中表明,訪問的時間周期越短,保持size/stride不變時,吞吐量會越大,所以由於三級緩存和主存訪問的時間周期長短不同,我們可以得知一級緩存吞吐量是大於二級緩存的,而二級緩存是大於三級緩存的。那為什么是在接近一級緩存大小時達到最大值呢?這也和我們學習的緩存命中等有關。如果工作集的大小越接近一級緩存,說明此時其緩存命中率就越高,這個應該很好理解。工作集大小很小時,往往需要訪問的數據在一級緩存中找不到,還需要在下一級的緩存中查找,這個過程也是很耗費時間的。所以工作集大小越接近一級緩存,命中率就越高,這樣也就減少訪問時間周期,從而也能解釋為何吞吐量在size達到一級緩存大小時達到最大值了。
然后當工作集大小不變,改變步長時,吞吐量是隨着步長的增加穩步下降的。舉個例子,就是當L1中的讀不命中會導致L2中的一個塊傳到L1中,后面在L1的這個塊上面也會有一定量的命中。而此時,隨着步長的增加,L1中命中與不不命中的比值也增加了,這也導致了吞吐量的降低。故可知隨着步長的逐步增加,吞吐量會下降。
最后就是我們做這個實驗是為了什么?想要學習到什么東西?從課本中我們也了解到了存儲器山它就是一座時間局部性與空間局部性的山,從不同的角度去進行分析,我們可以得到相應的結果。我們作為程序員,在了解了存儲器山之后,我們也應該也需要學會盡量去利用時間局部性和空間局部性原理,去讓我們的程序運行在“山峰”而不是“谷底”。利用課本上的一句話進行總結就是:利用時間局部性,使得頻繁使用的字從L1中取出,還要利用空間局部性,使得盡可能多的字從一個L1高速緩存中訪問到。
6. 實驗感想和總結
這個實驗由於測試代碼在課本上已經給出,其中缺少的fcyc2等文件也可以在GitHub等網站上找到,所以在代碼方面沒有難度,只需要正常的編譯運行即可。最后的結果也可以由MATLAB直接導入,同時在GitHub上也有相應的畫出存儲器山的代碼,畫圖的過程也沒有什么問題。
不過實驗過程中也有一些小插曲,就是關於fcyc2文件。我們在GitHub上找到了很多不同版本的fcyc2文件,它們的測試代碼都是一模一樣的,但是有的版本測出來的吞吐量及其少,只有幾百mb/s。但是這肯定與課本上的結果不符,因為雖然我個人電腦的CPU是i5的,課本上測試的是i7的,但是我的CPU的一級緩存和二級緩存都比它高,沒有理由會比課本上少。我們也檢查了fcyc2文件,但是我們也看不出什么端倪。后來換了一個版本的才恢復正常,有了上面的那幅圖。
總的來說,這個實驗沒有特別大的難度,也讓我對時間局部性和空間局部性也有了更深的一點了解。至於關於fcyc2的那個問題,之后如果有時間,可以更加深入的進行了解。
1. 實驗要求和目的
實驗要求:圖像增強操作中有一種衡量標准為均方差(MSE),均方差計算的步長為1。此實驗需要計算圖像大小從512到4096時,空間局部性對均方差計算時間的影響。
實驗目的:理解空間局部性,通過圖像說明空間局部性對程序運行時間的影響。
2. 實驗測試條件說明
實驗是在本人自己電腦上進行的測試,筆記本的相關信息如下:
型號: 宏碁 Acer Aspire VX5-591G
內存大小: 8G
CPU: Intel® Core™ i5-7300HQ CPU @2.50GHz 4核
高速緩存信息: L1 緩存:256KB
L2 緩存:1.0MB
L3 緩存:6.0MB
操作系統: Windows10 家庭版
3. 實驗過程
此題的要求雖然是對於圖像進行增強處理,但是實際上我們就可以把圖像看成是一個二維數組,如果是灰度圖,則二維數組里存的就是數值,如果是RGB圖,則二維數組中存放的是一個結構體,結構體中包含R,G,B三個數值。所以此題我們可以把其等價與有兩個二維數組,然后將兩個二維數組代入到MSE的公式中,求出運算的時間。
弄清楚這點后,代碼就可以很清楚的寫出來了,還有一點需要說明就是我們在計算時間時采用的是將程序運行10遍之后求平均時間這種方法,這樣可以避免一定的偶然性。
最后運行代碼,顯示最后運行的時間結果即可。
4. 實驗結果
從上面的結果可以看出基本上隨着塊大小以×2的速度增長時,花費的時間以近似2的平方的速率來增長。而我們從圖像也可以看出這個結果。(圖像的結果是用另一個結果,1.4ms,5.8ms,24.1ms,96.6ms,可以得出相同的結果)

5. 實驗分析
該試驗中由於只測試了從512到4096的數據,由於數組大小為512乘512時,花費的時間已經很短了,再減小數組大小測試也沒有什么意義。從數據中,我們可以看出,基本上數組大小以乘2的速度增長時(實際上對於二維數組,數組大小變成了原來的4倍),花費的時間也是上一個花費時間的4倍左右,基本上處於一個線性增長的過程。
從時間結果中我主要有一些看法。第一點是關於程序運行時間隨着數組大小線性增長。這說明我們是按照同樣的順序去訪問的數組,然后進行運算,在這點上程序的空間局部性的特點都是相同的。當然這需要是在同一級的存儲器當中。這些可以說明該程序在空間局部性上是做的比較好的。第二點的看法就是,其實從該題的實驗目的中我有一點點疑惑,不知道是不是我對題目的理解有誤。因為從無論是從實驗的代碼編寫和實驗的結果都可以看出,此題的實驗結果並不能特別的看出空間局部性對於程序執行時間的影響。
空間局部性一般是指如果程序訪問某個存儲器地址后,又在較短時間內訪問臨近的存儲器地址,則程序具有良好的空間局部性。然而在此實驗中,我們對於數組的訪問方式並沒有改變,僅僅只是改變數組的大小而已。所以影響程序運行時間很大程度上是取決於數組大小的,即時間局部性,而不是空間局部性。
如果需要考察空間局部性對程序運行時間的影響,我們只需要在MSE函數中調換循環的順序再運行即可。我也對此進行一下測試,測試的結果如下圖:
從此圖中可以看出,按列優先去訪問數組時,數組大小從512*512到4096*4096時,程序執行的時間分別是:2.2ms,12.7ms,54.4ms,293.2ms。而按行優先訪問時,時間分別為1.6ms,8.2ms,34.8ms,149.4ms。明顯從數據中可以看出按列優先訪問時間比按行優先訪問的時間要長得多。而數組存儲是按行優先存儲的,所以從兩組數據中對比可以看出空間局部性對程序運行時間的影響。
6. 實驗感想和總結
此次實驗我以為是需要真的去找一些圖片去實際進行測試,而當王建榮老師無意間看到我們做實驗后直接告訴我們就可以把圖像直接當成一個二維數組去看待我才想到這樣的方法。但是也正因為這樣的方法,導致了后面的問題,就是空間局部性對程序的執行時間沒有很大影響。
關於這個問題,后面是和同學討論的時候察覺到可以自己將循環的順序進行改變,這樣也能夠看出空間局部性對程序執行時間的影響。所以改變代碼后也得到了新的數據進行了對比,得出了正確的結論。這也讓我明白,有的時候一些看似復雜,摸不清頭緒的問題,換個思路可以很簡單的將之解決。
三.矩陣分塊相乘
1. 實驗要求和目的
實驗要求:在上課時的課件最后,介紹了分塊矩陣完成矩陣相乘。請實際編寫代碼,用實驗驗證,並分析分塊大小,矩陣大小等對性能的影響,最后需要繪制出結果的圖形。
實驗目的:驗證矩陣分塊相乘在性能上優於普通的矩陣相乘,並分析相關原因。
2. 實驗測試條件說明
實驗是在本人自己電腦上進行的測試,筆記本的相關信息如下:
型號: 宏碁 Acer Aspire VX5-591G
內存大小: 8G
CPU: Intel® Core™ i5-7300HQ CPU @2.50GHz 4核
高速緩存信息: L1 緩存:256KB
L2 緩存:1.0MB
L3 緩存:6.0MB
操作系統: Windows10 家庭版
3. 實驗過程
此實驗主要是探究分塊方法對於矩陣乘法效率的提升,然而對於一些比較小的矩陣,分塊與不分塊我們通過最后的數據並不能明顯的得到結果,所以我們此次實驗選用的矩陣大小是從512*512開始,每次SIZE*2,而塊從8*8開始一直到256*256為止。然后我們針對矩陣大小為512*512和1024*1024的兩種矩陣也做了對比分析(即也運行了未進行分塊時的結果)。整個實驗過程只要弄清楚了需要做什么,代碼是比較好寫的了。所以整個程序我們直接運行所寫的代碼,即可得到最后想要的結果,然后用MATLAB畫出對應的圖片即可。
4. 實驗結果
實驗結果主要是分為兩個部分,一個是未分塊時矩陣相乘的結果,一個是分塊時矩陣相乘的結果。由於未分塊時對於大小為2048*2048及以上的矩陣花費的時間太長,由於時間原因,對於這些數據也沒有進行測試。故在MATLAB繪制圖形時,也沒有繪制未分塊時的結果。具體的結果如下:
未分塊矩陣:
SIZE=512 時,花費時間為1282.7ms
SIZE=1024時,花費時間為32473.5ms
分塊矩陣:

具體結果用表格方式展示(單位為ms,Debug模式下)
| 8 |
16 |
32 |
64 |
128 |
256 |
|
| 512 |
826.8 |
859.5 |
801.1 |
765.8 |
1029.5 |
1038.4 |
| 1024 |
7765.2 |
7639.9 |
7229.5 |
9627.4 |
9669.4 |
10080.8 |
| 2048 |
62487.3 |
62319.2 |
61108.5 |
60891.3 |
77195.3 |
78500.7 |
| 4096 |
522227 |
570510 |
|
|
|
|
(圖形由於數量級差距較大,圖像可能不能直觀的反映出結果,結合數據看就好)
5. 實驗分析
由於在這個實驗過程中遇到的問題較多,所以在此想分為幾個方面來進行分析和討論。
(1)未分塊相乘與分塊相乘之間效率的對比
其實這點是很顯而易見的,通過上面的數據就可得知,在矩陣大小為512*512時,未分塊的時間為1282.7ms,而分塊的時間最長的也只需要1038.4ms。同理對於矩陣大小為1024*1024時也是一樣,未分塊是32473.5ms,而分塊時間最長也只需要10080.8ms。從實驗數據可以明顯看出,分塊矩陣相乘可以明顯的提高矩陣相乘的效率。
至於為什么能夠提高效率,這個原因其實很簡單。當數組大小逐漸增大時,高速緩存的不命中率會很明顯的增大,數組越大越明顯。而對矩陣進行分塊后,明顯的降低了不命中率,這樣的話計算起來花費的時候會明顯縮短。因為主要是從后一級的緩存或者主存中調數據到前面的緩存這個過程是很耗費時間的。而分塊相乘能明顯的減少這個過程,所以在以后矩陣相乘時,面對大小較大的矩陣時可以采用分塊相乘的方法。
(2)選取的塊大小對於程序執行時間的影響
探究選取的塊大小對程序運行時間的影響,我們就先固定矩陣大小不變。不過塊大小等於矩陣大小的話也沒有意義了,所以統一的設為8到256即可。我們從數據和圖形中可以看出。對於矩陣大小512*512和2048*2048時,塊大小從8到64增長時,花費的時間是逐漸減少的,而從64到128時會有一個跳躍性的增加。從這些數據中我們可以分析出大概在塊大小等於64(64*64)時,花費的時間最短。不過對於矩陣1024*1024是個例外,我們在后面再來說明這個問題。
在上課時,在PPT上面,我們也可以看到結果,對於矩陣分塊乘法,塊大小越大越好,不過需要有3B*B<C的限制。C是Cache size,即緩存大小,前面系數為3是因為在代碼中我們有兩個相乘的矩陣,還有第三個矩陣用來存儲他們相乘得到的結果。對於我的電腦,我的一級緩存L1大小為256KB(數據緩存部分為128KB),而當塊大小為64*64時,得到的結果是64*64*8*3=96KB,是滿足條件的。而塊大小變成128*128時,得到的結果為128*128*8*3=384KB,這樣就大於一級緩存的大小,此時就會發生不命中的現象,從而會導致運行時間有一個跳躍性的增長。所以在選取塊大小時,就在3B*B<C的限制下選取最大的塊大小即可。
關於矩陣大小為1024*1024,花費時間發生跳躍性增長是在塊大小從32變到64時發生的。本來之前也是對這個問題有一些疑惑,但是在寫報告后與同學進行了討論,對程序重新進行編譯運行,只是這次並沒有采用Debug方式,而是采用了Release方式進行運行,得到了不太一樣的結果。結果以表格的形式展示如下面的表格和圖像(由於對於release模式時間普遍都有減少,所以在此運行了矩陣大小為4096*4096時的各項數據的結果)。
從結果中可以看出,在release模式下進行運行時,時間縮短了很久。這是因為Debug模式下它包含很多調試信息,並且對程序運行的速度沒有做過多的優化。而相比Debug模式,Release它在速度上進行了優化,但是它沒有對源代碼進行調試,這主要是兩者的差異。在其它實驗中我都是采用Debug模式,但是由於此實驗的特殊性,在此也用Release模式運行一次,但是不管用哪個模式,數據的分布規律等理論上來說應該是一樣的。
從上面我們可以得出,一般選擇塊時是在3B*B<C的限制下選取最大的塊,但是實際上結果與我們預計的有點不符,從下面的結果可以看出,在塊大小為32*32和64*64時結果都差不太多,所以並不是塊大小越大越好,可能根據不同的機器會有不同的情況出現,但是可以肯定的是,在滿足3B*B<C的條件下,塊偏大一點運行的時間還是會快一點,但是不是最大的就是最快的。
(Release模式下,單位為ms)
| SIZE\BLOCK |
8 |
16 |
32 |
64 |
128 |
256 |
| 512 |
261.5 |
248.2 |
238.5 |
237 |
260.5 |
262.5 |
| 1024 |
1977.5 |
1751.5 |
1706.3 |
1760 |
1944.2 |
1917.6 |
| 2048 |
17726.9 |
15108.1 |
14263.9 |
14507 |
15826.4 |
16070.1 |
| 4096 |
156833 |
123672 |
112854 |
111803 |
125633 |
130909 |
(3)矩陣的大小對程序執行時間的影響
關於這個問題是顯而易見的,在塊大小相同時,矩陣大小越大,分出來的塊數量,在我們可以認為每一個塊內執行乘法的時間是相同的條件下,塊的數量越多,花費的時間也就越長。而在實際過程中由於緩存不命中等原因,可能塊越多花費的時間更長。
所以矩陣越大,程序執行的時間越長。
(4)關於4096*4096矩陣大小的一些問題
實際上在運行程序時,我矩陣大小最大的時候設為了4096*4096。但是實際運行程序時發現這樣運行程序有點不太現實了。從上面的結果可以看出,分塊大小為8*8時,花費的時間為522227ms,即522.227s,將近9分鍾,而我是利用程序運行10遍取平均值的機制,一個塊我需要運行1個半小時,從8到256是6個測試數據,所以保守估計需要9個小時。然而在程序的運行過程中,過長的時間會導致很多問題,如突然間程序不占用CPU,或者突然死機等問題。我花費兩天的時間運行了兩次,都因為一些原因而沒有跑出結果。后來是在Release模式下終於得到了結果,與預期的也比較相符。這也讓我明白了,在遇到一些運行時間比較長的程序時,如果需要對其進行一些分析,可以用Release模式來運行程序。
6. 實驗感想和總結
這個實驗應該算是我四個大實驗中花費時間最多的一個實驗了,因為其中遇到了各種各樣的問題,導致不斷的去進行各種各樣的測試,修改代碼,更換系統等。雖然大部分的疑惑都已經解決,但是仍然存在着一些問題。不過我本人很享受這個過程,在這其中我體會到不斷的找出問題,解決問題不斷反復的過程對我的思考問題的方式有很大的提升。同時我對存儲器的結構層次等有了更深入的了解。
在我完成這份報告后,我找到了前面遺留問題的一些解決方式,於是又返回來對報告重新進行了修改。這個過程也讓我收獲很多,在不斷的交流中不斷思考,找出其中的一些解決方案,還有就是不能太過於相信理論,認為和課本上不符的就一定是錯的。畢竟程序運行時還有一些別的條件的影響,這次用Debug模式和Release模式就是一個很好的例子,希望自己以后也能夠注意這一點。
四.矩陣轉置
1. 實驗要求和目的
實驗要求:目前的矩陣轉置在傳統方法上總會存在着一個較大的Cache miss,所以本實驗以灰度圖為例,利用程序的局部性原理,想出兩種方法來提高矩陣轉置的效率,並用C/C++語言編寫代碼進行測試,對實驗結果要有分析說明,並繪制出相應的圖形。
實驗目的:這個實驗主要目的就是考察對時間局部性和空間局部性的理解,然后根據自身理解去尋找能夠提高矩陣轉置的效率的方法。檢測自己是否能夠將所學習的知識舉一反三,靈活運用。
2. 實驗測試條件說明
實驗是在本人自己電腦上進行的測試,筆記本的相關信息如下:
型號: 宏碁 Acer Aspire VX5-591G
內存大小: 8G
CPU: Intel® Core™ i5-7300HQ CPU @2.50GHz 4核
高速緩存信息: L1 緩存:256KB
L2 緩存:1.0MB
L3 緩存:6.0MB
操作系統: Windows10 家庭版
3. 實驗過程
本實驗老師也給了一個網站去進行參考,但是網站上給出的SSE算法優化矩陣轉置不是從程序局部性原理來解決問題的,也只能結合自身學習到的知識來想出解決的方法。
程序的局部性原理,在我們所學的知識中主要是分為時間局部性和空間局部性兩種。把這兩種知識運用到此實驗中對應想出兩種解決方案。時間局部性改進主要是提高緩存的命中率,而矩陣大小越大,緩存的命中率就越低,所以自然而然結合上一個分塊矩陣乘法的時間想到可以進行分塊矩陣轉置。而對於空間局部性,主要是盡量的按照矩陣的存儲方式去讀寫方能有良好的效率,但是此題比較特殊,不管采用哪種方式,在原矩陣和目標矩陣中,總會有一個矩陣的訪問順序是與存儲方式相反的。但是從這點中我們也可以找出一些改進方式。我們通常的方式就是按照原矩陣(需要轉置的矩陣)的存儲方式先行后列循環,這樣對於目標矩陣就是先列后行的順序。但是如果我們轉換一下,按照目標矩陣先行后列循環會不會效率高一點呢?根據后面的測試,可以給出肯定的答案。
所以我們根據分析得到了兩種方法。一種是分塊矩陣轉置,一種是改變循環順序,按照目標矩陣先行后列的順序進行轉置。
明確兩種方式之后,我們就開始編寫代碼,主要是三個函數,原函數,分塊轉置函數,改變循環順序的函數。編寫完成后依次運行三個函數,得出運算結果,進行對比分析。
4. 實驗結果
實驗結果分為幾個部分,首先是沒有采用任何方法平常的矩陣轉置(對應函數trans1),第二種是對循環順序進行改變的結果(對應函數trans2),第三種是分塊矩陣轉置的結果(對應函數trans3)。在下面以表格的方式進行展示。實驗測試為了方便,測試時矩陣均使用方陣。
在此說明,本實驗在運行時使用的時Release模式,用Debug模式運行時,數據會比較奇怪,在后面實驗分析中會進行說明。
(數據單位為ms)
| 函數\ SIZE |
256*256 |
512*512 |
1024*1024 |
2048*2048 |
4096*4096 |
| Trans1 |
0.3 |
1.6 |
8.4 |
54.6 |
287.7 |
| Trans2 |
0.1 |
1 |
6.6 |
51.5 |
194.3 |
| Trans3: 8 |
0.2 |
1.2 |
6.8 |
49.4 |
211 |
| Trans3: 16 |
0.1 |
0.8 |
6.3 |
44.7 |
207.4 |
| Trans3: 32 |
0.1 |
0.8 |
7.7 |
44 |
207 |
| Trans3: 64 |
0.1 |
1.4 |
6.3 |
43.3 |
207.3 |
| Trans3: 128 |
0 |
1.6 |
6.2 |
43 |
208.2 |
| Trans3: 256 |
0.1 |
1.2 |
6.3 |
43 |
207.4 |


5. 實驗分析
由於此實驗涉及到的方面也比較多,在此也分為幾點來進行說明。
(1) Debug模式下和Release模式下的數據差別
在之前也已經提到過了兩者的區別了,Release模式是對程序運行速度有優化的。我在兩種模式下運行出來的結果如下(沒有截圖)。
Debug:
Trans1 0.4 3.3 17.3 85.7 397.3
Trans2 0.2 2.7 16.7 93 331.2
Release:
Trans1 0.3 1.6 8.4 54.6 287.7
Trans2 0.1 1 6.6 51.5 194.3
可以看到在Debug模式下對於矩陣大小為2048*2048時,改進的轉置方法的用時反而大於未改進的。關於這一點,我多次運行的結果仍然是這樣,這讓我很奇怪,但是我又想不到能夠解釋這種情況的原因。多次嘗試無果后,我采用Release模式運行時,結果就符合預期了,並且運行多次也是這個結果。關於這個現象,可能是我對這兩種VS里的運行模式還不清楚,這個問題以后會繼續進行研究,在此由於不影響最后的實驗結果,就不多描述了。
(2)未改進的矩陣轉置與在空間局部性上改進的矩陣轉置的對比
在之前實驗過程中,我們已經說明了空間局部性的改進是將原來的按原矩陣先行后列的方式改為按目標矩陣先行后列的方式進行循環。從結果中我們看出,對於此改進,在矩陣大小越大時,效果越明顯,后者所執行的時間的確比前者要短,證明這個改進是有效的。
對此我個人有種分析。按照原來的方式,按照原矩陣先行后列,這樣是在讀取原矩陣時有良好的空間局部性,但是對於目標矩陣,寫不命中率很高。而交換順序后,時讀取原矩陣時的讀不命中率很高。個人感覺按照過程分析的話寫不命中后的懲罰時間要比讀不命中后的懲罰時間要長。所以在此是優先保證寫命中,這樣會在原來的基礎上減少程序的運行時間,而結果也正是如此。
關於這個觀點,后續還會繼續進行探索研究,找出更准確實際的證據。
(3)未改進的矩陣轉置與在時間局部性上改進的矩陣轉置的對比
時間局部性的改進,就是將矩陣進行分塊再在每塊中進行轉置。這樣在時間局部性上有更好的表現,降低了其緩存的不命中率。這種方法也是根據前面的分塊矩陣乘法得到的思路。
從結果上來看,分塊矩陣轉置的確能夠減少程序所運行的時間,但是從結果中來看,與分塊的大小並沒有特別大的關系,塊大小變化時,運行時間的變化不是很明顯。這點也可作為繼續研究的一項工作。
總而言之,即分塊轉置能夠提高矩陣轉置的效率,但是分塊的大小對程序執行時間的影響並沒有很大。
6. 實驗總結與感想
這個實驗也是花費了很多時間去進行測試和分析,其中也解決了很多問題,但也遺留了一些問題。在這個過程中我體會到,需要敢於提出自己的觀點,如果連觀點都沒有,更不可能解決問題。提出觀點后可以與別人討論,探究,看方法是否可行,這個過程也是我最享受的過程,這讓我覺得學到的知識還是有用的。希望今后能夠保持這種態度,在更多的問題上去進行探索和研究。
在課本中的小實驗就不需要像大實驗那樣介紹的如此詳細了。所以在此把小實驗都寫在一起即可。
1. 課后習題6.7
題目要求:對一個三維數據進行求和,計算出程序運行的執行時間。同時需要改變循環的順序和數組的大小,將得到的結果繪制成圖形。
題目目的:主要是觀察分析自己電腦的CPU的效率,加深自己對空間局部性等的理解。
實驗過程:書上已經給出我們詳細的代碼,只需要編寫主函數和計算時間的clock變量即可。編寫完代碼過后直接運行即可得到我們需要的結果。
實驗結果: 由於小實驗是很久之前就運行完,當時忘記了截圖,只進行了手動記錄,在此以表格的形式進行展示。
(數據單位為ms)
| 順序\N值 |
10 |
50 |
100 |
500 |
1000 |
| i, j, k |
0 |
0.5 |
3.4 |
290.2 |
2303.6 |
| i, k, j |
0 |
0.7 |
4.1 |
521.5 |
5072.6 |
| j, i, k |
0 |
0.5 |
4.3 |
346.9 |
2511.1 |
| j, k, i |
0 |
0.4 |
6.4 |
943.6 |
13791.9 |
| k, i, j |
0 |
0.6 |
11.8 |
2636.0 |
22026.7 |
| k, j, i |
0 |
0.8 |
10.7 |
3169.8 |
30144.9 |
實驗分析:
此題最初老師給的要求是N的大小從10到10W來進行增長,但是在實際的運行過程中在N=1000時,通過VS看到的內存占用就已經達到了3G,而我的電腦的內存才8G。所以運行最多到N=1000是極限了。
從圖中的數據可以看出,循環順序按照i, j, k進行運行時,花費的時間最短,按照k, j, i的順序運行時花費的時間最長。在我們課上學到過,數組的存儲是以行優先的方式存儲的,對於三維數組,依次類推應為行最優先,列其次,縱向最次。所以i, j, k是按照最優的順序訪問的,這樣程序就具有良好的空間局部性,所以花費的時間就最短,而其它的順序都有和最優順序不同的地方,而k, j, i是完全與其順序相反,這樣的空間局部性是最差的,所以花費的時間也就最長。
本題的主要目的就是探究空間局部性對程序執行時間的影響,而最終得到的實驗結果也符合我們的預期。
實驗感想:
此題其實沒什么難度,但是由於最開始沒有仔細研究數組所占的空間大小,導致一開始直接用10000去試運行程序,為此付出的代價就是電腦死機,然后重裝系統。所以對於這類問題以后還是先想清楚一些細節問題為妙。
對於實驗本身的結果,我個人沒有任何異議。結果符合預期,也能夠很好的證明空間局部性對程序運行時間的影響。這也讓我們學到了以后在編寫代碼時如果能多加考慮這個問題的話,能讓程序更快速的運行。
2. 課后習題6.8
題目要求:運行習題6.8中的三段代碼,同時改變數組的大小N,將得到的結果繪制成圖形,並分析說明。
題目目的:主要是加深對空間局部性的了解,了解空間局部性對程序執行時間的影響。
實驗過程:直接按照書上給出的代碼進行程序編寫,然后改變N值的大小,運行程序將得到的結果繪制成圖形即可。
實驗結果:該程序也只是簡單的做了一下數據的記錄,沒有截圖,以表格的方式展示。
| 函數\N大小 |
1000 |
10000 |
100000 |
1000000 |
| Clear1 |
0 |
0 |
2 |
18 |
| Clear2 |
0 |
1 |
1 |
10 |
| Clear3 |
0 |
0 |
7 |
53 |
實驗分析:
其實此題的結果與理論上的分析有些出入。在理論上,函數clear1以步長為1的引用模式訪問數組,是具有最好的空間局部性的。函數clear2在每個結構中,以步長不為1的模式跳到下列相對於結構起始位置的偏移處,所以函數clear2的空間局部性相較於clear1來說要差一點。而函數clear3,不僅在結構中跳來跳去,還在結構之間跳躍,所以函數clear3的空間局部性最差。
單從代碼上我們可以作如上分析,但是實際的運行結果而言,函數clear2的運行時間卻是最短的,不管是用Debug模式還是Release模式均是如此,所以這與我們之前分析的空間局部性的良好程度不太符合。
關於這個原因,可能是個人的猜想,或許是函數clear1中相比於clear2中多使用的一個循環的原因。可能一個循環執行的時間要比在結構體中跳躍到偏移處的時間要長,所以才導致實際運行起來函數clear2的時間要短於函數clear1的時間。關於驗證尚還沒有找到合適的方法。
這個例子也印證了一點,不是具有良好的空間局部性的代碼就一定有着最快的運行時間。有的時候也受其它的一些因素影響。
實驗總結和感想:
此題的實驗結果與理論上分析的結果不相符,這也是讓我花費了很大氣力。不同系統下進行了測試,不同的運行模式等等。這也讓我明白,理論分析也不一定就和實際結果相符,兩者不符合時,也需要我們去找出一些能夠解釋這些現象的理由,這也是對我們的一種考驗。
3. 課后習題6.17
題目要求:測試習題6.17的矩陣轉置代碼執行所需要的時間。需要改變矩陣大小進行多次測試,最后繪制出圖形。
題目目的:主要是觀察分析自己電腦的CPU的效率,加深自己對時間局部性等的理解。
實驗過程:書上已經給出我們詳細的代碼,只需要編寫主函數和計算時間的clock變量即可。編寫完代碼過后直接運行即可得到我們需要的結果。
實驗結果:該程序也只是簡單的做了一下數據的記錄,並沒有截圖,仍然以表格的方式展示。
(數據單位為ms)
| 矩陣大小 |
2 |
4 |
8 |
16 |
32 |
64 |
128 |
256 |
| 用時 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0.2 |
| 512 |
1024 |
2048 |
4092 |
8192 |
16384 |
| 1 |
4.3 |
27.8 |
176.1 |
1085.1 |
6244.3 |
實驗分析:
該實驗實際上結果也是符合預期的,在不改變循環的順序時,僅僅改變矩陣的大小,會導致時間局部性變差,因為緩存的不命中率會越來越高。所以在循環順序不變時,矩陣大小增大時,運行的時間會逐漸變長。而結果也是符合理論的。
實驗感想和總結:
本實驗僅僅只是測試時間局部性對程序運行結果的影響,根據得出的數據,然后結合所學的知識,很容易得出數據結果是符合理論的。也可以看出時間局部性對程序運行時間的影響,所以以后編寫程序時,要注意盡量保證緩存的命中率。
六.課本課后作業
習題6.37
在N=64時,sumA的不命中率為0.25, sumB為1, sumC為0.5
在N=60時,三者的不命中率都為0.25
習題6.41
每行4個字節,循環時后三個命中,第一個不命中,不命中率為0.25.
