實(shí)驗(yàn)報告
實(shí) 驗(yàn)(三)
題???? 目 ?????優(yōu)化??????? ???????
專?????? 業(yè) ???人工智能(未來技術(shù))???
學(xué) ?? 號 ???7203610716?????????????
班 ?? 級 ???20WJ102???????????????
學(xué)?????? 生 ???孫銘蔚???????????
指 導(dǎo) 教 師 ???劉宏偉???????????????
實(shí) 驗(yàn) 地 點(diǎn) ???G712?????????????
實(shí) 驗(yàn) 日 期 ???2022.04.16?????????
計算學(xué)部
目? 錄
第1章 實(shí)驗(yàn)基本信息....................................... - 3 -
1.1 實(shí)驗(yàn)?zāi)康?.................................................. - 3 -
1.2 實(shí)驗(yàn)環(huán)境與工具............................................. - 3 -
1.2.1 硬件環(huán)境............................................... - 3 -
1.2.2 軟件環(huán)境............................................... - 3 -
1.2.3 開發(fā)工具............................................... - 3 -
1.3 實(shí)驗(yàn)預(yù)習(xí)................................................... - 3 -
第2章 實(shí)驗(yàn)預(yù)習(xí)........................................... - 4 -
2.1 程序優(yōu)化的十大方法(5分).................................. - 4 -
2.2性能優(yōu)化的方法概述(5分).................................. - 4 -
2.3 Linux下性能測試的方法(5分)............................... - 4 -
2.4 Windows下性能測試的方法(5分).............................. - 4 -
第3章 性能優(yōu)化的方法..................................... - 5 -
第4章 性能優(yōu)化實(shí)踐....................................... - 7 -
第5章 總結(jié).............................................. - 8 -
5.1 請總結(jié)本次實(shí)驗(yàn)的收獲....................................... - 8 -
5.2 請給出對本次實(shí)驗(yàn)內(nèi)容的建議................................. - 8 -
參考文獻(xiàn)................................................. - 9 -
第1章 實(shí)驗(yàn)基本信息
1.1 實(shí)驗(yàn)?zāi)康?/h3>
-
-
理解程序優(yōu)化的10個維度
- 熟練利用工具進(jìn)行程序的性能評價、瓶頸定位
- 掌握多種程序性能優(yōu)化的方法
- 熟練應(yīng)用軟件、硬件等底層技術(shù)優(yōu)化程序性能
1.2 實(shí)驗(yàn)環(huán)境與工具
1.2.1 硬件環(huán)境
- 理解程序優(yōu)化的10個維度
- 熟練利用工具進(jìn)行程序的性能評價、瓶頸定位
- 掌握多種程序性能優(yōu)化的方法
- 熟練應(yīng)用軟件、硬件等底層技術(shù)優(yōu)化程序性能
硬件1概覽:
? 型號名稱: MacBook Air
? 型號標(biāo)識符: MacBookAir10,1
? 芯片: Apple M1
? 核總數(shù): 8(4性能和4能效)
? 內(nèi)存: 8 GB
? 系統(tǒng)固件版本: 7429.81.3
? 操作系統(tǒng)加載程序版本: 7429.81.3
? 序列號(系統(tǒng)): C02H479LQ6L5
? 硬件UUID: B9297F28-81B7-5DB0-8760-89063F63E0E5
? 預(yù)置UDID: 00008103-001205960108801E
? 激活鎖狀態(tài): 已啟用
硬件2概覽:
64 位操作系統(tǒng), 基于 x64 的處理器
AMD Ryzen 7 4800H with Radeon Graphics??????????? 2.90 GHz
LAPTOP-CSSCDDGT
1.2.2 軟件環(huán)境
Windows下Visual Studio 2020以上64位
Windows下Perf、VS的性能探測器
Ubuntu下oprofiler、gprof
1.2.3 開發(fā)工具
Visual Studio 2022 64位;vs code 64位
1.3 實(shí)驗(yàn)預(yù)習(xí)
了解實(shí)驗(yàn)的目的、實(shí)驗(yàn)環(huán)境與軟硬件工具、實(shí)驗(yàn)操作步驟,復(fù)習(xí)與實(shí)驗(yàn)有關(guān)的理論知識。性能測試準(zhǔn)確性的文獻(xiàn)查找,我了解到,流水線、超線程、超標(biāo)量、向量、多核、GPU、多級CACHE、編譯優(yōu)化Ox、多進(jìn)程、多線程等多種因素對程序性能均有綜合影響。
第2章 實(shí)驗(yàn)預(yù)習(xí)
總分20分
2.1 程序優(yōu)化的十大方法(5分)
- 優(yōu)化性能
- 減少空間占用,減少運(yùn)行內(nèi)存
- 美化UI界面
- 更加正確
- 增加程序可靠性
- 增加可移植性
- 有更強(qiáng)大功能
- 使用方便
- 更加符合編程規(guī)范和接口規(guī)范
- 更加易懂,加注釋、模塊化
2.2性能優(yōu)化的方法概述(5分)
1.一般有用的優(yōu)化
2.面向編譯器的優(yōu)化
3.面向超標(biāo)量CPU的優(yōu)化
4.面向向量CPU的優(yōu)化
5. CMOVxx等指令
6. 嵌入式匯編
7.面向編譯器的優(yōu)化
8.面向存儲器的優(yōu)化:
9.內(nèi)存作為邏輯磁盤
10.多進(jìn)程優(yōu)化
11.文件訪問優(yōu)化:帶Cache的文件訪問
12.并行計算:多線程優(yōu)化
13.網(wǎng)絡(luò)計算優(yōu)化
14.GPU編程、算法優(yōu)化
15.超級計算
2.3 Linux下性能測試的方法(5分)
- linux下使用Oprofile等工具
- 使用自己構(gòu)造的函數(shù)測出程序運(yùn)行時間,進(jìn)而測試性能
2.4 Windows下性能測試的方法(5分)
1.在visual studio中使用調(diào)試工具:性能探測器:CPU、RAM、GPU
2.使用自己構(gòu)造的函數(shù)測出程序運(yùn)行時間,進(jìn)而測試性能
第3章 性能優(yōu)化的方法
總分20分
逐條論述性能優(yōu)化方法名稱、原理、實(shí)現(xiàn)方案(至少10條)
3.1
1.一般有用的優(yōu)化
代碼移動
復(fù)雜指令簡化
公共子表達(dá)式
2.面向編譯器的優(yōu)化:障礙
函數(shù)副作用
內(nèi)存別名
3.面向超標(biāo)量CPU的優(yōu)化
流水線、超線程、多功能部件、分支預(yù)測投機(jī)執(zhí)行、亂序執(zhí)行、多核:分離的循環(huán)展開!
只有保持能夠執(zhí)行該操作的所有功能單元的流水線都是滿的,程序才能達(dá)到這個操作的吞吐量界限
4.面向向量CPU的優(yōu)化:MMX/SSE/AVR
5. CMOVxx等指令
代替test/cmp+jxx
6. 嵌入式匯編
7.面向編譯器的優(yōu)化
Ox:0 1 2 3 g
8.面向存儲器的優(yōu)化:Cache無處不在
重新排列提高空間局部性
分塊提高時間局部性
9.內(nèi)存作為邏輯磁盤:內(nèi)存夠用的前提下。
10.多進(jìn)程優(yōu)化
fork,每個進(jìn)程負(fù)責(zé)各自的工作任務(wù),通過mmap共享內(nèi)存或磁盤等進(jìn)行交互。
11.文件訪問優(yōu)化:帶Cache的文件訪問
12.并行計算:多線程優(yōu)化:第12章
13.網(wǎng)絡(luò)計算優(yōu)化:第11章、分布式計算、云計算
14.GPU編程、算法優(yōu)化
15.超級計算
?
第4章 性能優(yōu)化實(shí)踐
總分60分
4.1 原始程序及說明(10分)
void putong(long img[1922][1082])
{
??? for(int j = 1; j < 1081; j++)
??? {
??????? for (int i = 1; i < 1921;i++)
??????? {
??????????? /* code */
??????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) / 4;
??????? }
??? }
}
程序的功能:
源程序功能是對給定的數(shù)組模擬圖像平滑算法,對數(shù)組的值進(jìn)行更改
流程:任一點(diǎn)的顏色值為其上下左右4個點(diǎn)顏色的平均值,即:img[i][j]? =?? (? img[i-1][j] + img[i+1][j] +img[i][j-1] + img[i][j+1] ) /4。
程序可能瓶頸:
1、這個程序不符合空間局部性
2、這個程序使用了占用時鐘周期較長的除法
3、這個程序沒有充分利用CPU,即流水線不是總處于滿的狀態(tài),具有數(shù)據(jù)相關(guān)性。由于循環(huán)減少了分支預(yù)測失敗的可能性,同時增加了循環(huán)體內(nèi)語句并發(fā)執(zhí)行的可能性,我們考慮使用循環(huán)展開對代碼優(yōu)化。
4.2 優(yōu)化后的程序及說明(20分)
至少包含面向CPU、Cache的兩種優(yōu)化策略(20分),額外每增加1種優(yōu)化方法加5分至第4章滿分。
面向CPU(循環(huán)展開):
void cpu(long img[1922][1082])//循環(huán)展開
{
? ? int i;
? ? long bef1,aft1,bef2,aft2;
? ? long B[1082];
? ? for (i = 0;i < 1082;i++) {
? ? ? ? B[i] = img[0][i];
? ? }
? ? for(int j=1;j<1080;j+=8)
? ? {
? ? ? ? for(int i=1;i<1921;i++)
? ? ? ? {
? ? ? ? ? ? bef1=img[i][j+1];
? ? ? ? ? ? aft1=img[i][j+2];
? ? ? ? ? ? bef2=img[i][j+5];
? ? ? ? ? ? aft2=img[i][j+6];
? ? ? ? ? ? img[i][j]=(B[j]+img[i+1][j]+img[i][j-1]+bef1)/4;
? ? ? ? ? ? img[i][j+1]=(B[j+1]+img[i+1][j+1]+aft1+img[i][j])/4;
? ? ? ? ? ? img[i][j+2]=(B[j+2]+img[i+1][j+2]+bef1+img[i][j+3])/4;
? ? ? ? ? ? img[i][j+3]=(B[j+3]+img[i+1][j+3]+aft1+img[i][j+4])/4;
? ? ? ? ? ? img[i][j+4]=(B[j+4]+img[i+1][j+4]+img[i][j+3]+bef2)/4;
? ? ? ? ? ? img[i][j+5]=(B[j+5]+img[i+1][j+5]+aft2+img[i][j+4])/4;
? ? ? ? ? ? img[i][j+6]=(B[j+6]+img[i+1][j+6]+bef2+img[i][j+7])/4;
? ? ? ? ? ? img[i][j+7]=(B[j+7]+img[i+1][j+7]+aft2+img[i][j+8])/4;
? ? ? ? ? ? B[j] = img[i][j];
? ? ? ? ? ? B[j + 1] = img[i][j+1];
? ? ? ? ? ? B[j + 2] = img[i][j+2];
? ? ? ? ? ? B[j + 3] = img[i][j+3];
? ? ? ? ? ? B[j+4] = img[i][j+4];
? ? ? ? ? ? B[j + 5] = img[i][j+5];
? ? ? ? ? ? B[j + 6] = img[i][j+6];
? ? ? ? ? ? B[j + 7] = img[i][j+7];
? ? ? ? }
? ? }
}
說明:
顯然流水線只要流起來可以大大提高CPU的工作效率,我們在循環(huán)的時候,每次可以使用更多變量參與運(yùn)算,能使流水線更好地流。于是。我首先講邊緣數(shù)據(jù)存儲到數(shù)組B中。在主循環(huán)中,我設(shè)置的步長為8,我定義4個變量aft1和bef1,aft2和bef2,分別存儲8個原數(shù)據(jù)的第3、2、7、6個,每次循環(huán)更新img數(shù)組后,更新B數(shù)組。
這樣可以使硬件能讓流水線很好地運(yùn)作,即使在少數(shù)情況下也只犧牲一點(diǎn)效率。
面向Cache1(符合空間局部性):
void cache(long img[1922][1082])
{
??? for(int i = 1; i < 1921;i++)
??? {
??????? for (int j = 1; j < 1081; j++)
??????? {
??????????? /* code */
??????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) /4;
??????? }
??? }
}
說明:
由于數(shù)組在電腦中按行進(jìn)行存儲,根據(jù)上課講過的命中和不命中的知識,通過書中知識可以知道,對于數(shù)組來說,一旦程序訪問了某個存儲單元,在不久之后,其附近的存儲單元也將被訪問,即程序在一段時間內(nèi)所訪問的地址,可能集中在一定的范圍之內(nèi),這是因?yàn)橹噶钔ǔJ琼樞虼娣?、順序?zhí)行的,數(shù)據(jù)也一般是以向量、數(shù)組、表等形式簇聚存儲的。如果一個存儲器的位置被引用,那么將來他附近的位置也會被引用。如果其訪問順序和存儲順序一致,程序性能會提升很多。
面向Cache2(按行進(jìn)行分塊):
void fenkuai_cpu_row(long img[1922][1082])
{
??? int i, j, k, kk=4, jj=4;
??? int bsize = 4;
??? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
??? int en = 1921; /* Amount that fits evenly into blocks */
??? for (jj = 1; jj < en; jj += bsize)
??? {
??????? for (i = jj; i < jj + bsize; i++)
??????? {
??????????? for (j = 1; j < 1081; j++)
??????????? {
??????????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) / 4;
??????????? }
??????? }
??? }
}
說明:
根據(jù)csapp中所述,有一種有趣的技術(shù)叫做分塊,它可以改善內(nèi)部循環(huán)的時間局部性。分塊的一般思想是將程序中的數(shù)據(jù)結(jié)構(gòu)組織成稱為塊的大塊。(在這里,“塊”指的是應(yīng)用程序級的數(shù)據(jù)塊,而不是緩存塊。)程序是結(jié)構(gòu)化的,它將一個數(shù)據(jù)塊加載到L1緩存中,對該數(shù)據(jù)塊執(zhí)行所有需要的讀寫操作,然后丟棄該數(shù)據(jù)塊,加載下一個數(shù)據(jù)塊,以此類推。與用于改進(jìn)空間局部性的簡單循環(huán)轉(zhuǎn)換不同,阻塞使代碼更難閱讀和理解。由于這個原因,它最適合優(yōu)化編譯器或頻繁執(zhí)行的庫例程。利用上述原理,我將數(shù)據(jù)按列分塊,我默認(rèn)一個塊中有4個數(shù)據(jù),進(jìn)行試驗(yàn)后發(fā)現(xiàn)性能提升不少。
面向Cache3(按列進(jìn)行分塊):
void fenkuai_cpu_col(long img[1922][1082])
{
??? int i, j, k, kk=4, jj=4;
??? int bsize = 4;
??? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
??? int en = 1081; /* Amount that fits evenly into blocks */
??? for (jj = 1; jj < en; jj += bsize)
??? {
??????? for (i = 1; i < 1921; i++)
??????? {
??????????? for (j = jj; j < jj + bsize; j++)
??????????? {
??????????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) / 4;
??????? ????}
??????? }
??? }
}
說明:
根據(jù)csapp中所述,有一種有趣的技術(shù)叫做分塊,它可以改善內(nèi)部循環(huán)的時間局部性。分塊的一般思想是將程序中的數(shù)據(jù)結(jié)構(gòu)組織成稱為塊的大塊。(在這里,“塊”指的是應(yīng)用程序級的數(shù)據(jù)塊,而不是緩存塊。)程序是結(jié)構(gòu)化的,它將一個數(shù)據(jù)塊加載到L1緩存中,對該數(shù)據(jù)塊執(zhí)行所有需要的讀寫操作,然后丟棄該數(shù)據(jù)塊,加載下一個數(shù)據(jù)塊,以此類推。與用于改進(jìn)空間局部性的簡單循環(huán)轉(zhuǎn)換不同,阻塞使代碼更難閱讀和理解。由于這個原因,它最適合優(yōu)化編譯器或頻繁執(zhí)行的庫例程。利用上述原理,我將數(shù)據(jù)按列分塊,我默認(rèn)一個塊中有4個數(shù)據(jù),進(jìn)行試驗(yàn)后發(fā)現(xiàn)性能提升不少。
面向Cache4(按正方形進(jìn)行分塊):
void fenkuai_cpu_row4(long img[1922][1082])
{
? ? int i, j, k, kk = 4, jj = 4;
? ? int bsize = 4;
? ? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
? ? int M = 1081, N = 1921;
? ? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
? ? int en = 1921; /* Amount that fits evenly into blocks */
? ? for (int ii = 1; ii < N; ii += bsize)
? ? {
? ? ? ? for (int jj = 1; jj < M; jj += bsize)
? ? ? ? {
? ? ? ? ? ? for (int i = ii; i < ((ii + bsize) > N ? N : ii + bsize); i++)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (int j = jj; j < ((jj + bsize) > M ? M : jj + bsize); j++)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? img[i][j] = (img[i - 1][j] + img[i + 1][j] + img[i][j - 1] + img[i][j + 1]) / 4;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
一般的優(yōu)化方法(除法變成移位操作):
void cache1(long img[1922][1082])
{
??? for(int j = 1; j < 1081; j++)
??? {
??????? for (int i = 1; i < 1921;i++)
??????? {
??????????? /* code */
??????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) >> 2;
??????? }
??? }
}
說明:
由于從效率上看,使用移位指令有更高的效率,因?yàn)?/span>移位指令占2個機(jī)器周期,而乘除法指令占4個機(jī)器周期。從硬件上看,移位對硬件更容易實(shí)現(xiàn),所以會用移位,左移一位就乘2,右移一位處以2,這種乘除法考慮移位實(shí)現(xiàn)會更快。
整合所有方法:
void success(long img[1922][1082])
{
? ??int i;
??? long bef,aft;
??? long B[1082];
??? for (i = 0;i < 1082;i++) {
??????? B[i] = img[0][i];//存儲邊界值
??? }
??? for(int i=1;i<1921;i++)
??? {
??????? for(int j=1;j<1081;j+=4)
??????? {
??????????? bef=img[i][j+1];
??????????? aft=img[i][j+2];
????? ??????img[i][j]=(B[j]+img[i+1][j]+img[i][j-1]+bef)>>2;
??????????? img[i][j+1]=(B[j+1]+img[i+1][j+1]+aft+img[i][j])>>2;
??????????? img[i][j+2]=(B[j+2]+img[i+1][j+2]+bef+img[i][j+3])>>2;
??????????? img[i][j+3]=(B[j+3]+img[i+1][j+3]+aft+img[i][j+4])>>2;
? ??????????B[j] = img[i][j];
??????????? B[j + 1] = img[i][j+1];
??????????? B[j + 2] = img[i][j+2];
??????????? B[j + 3] = img[i][j+3];
??????? }
??? }
}
4.3 優(yōu)化前后的性能測試(10分)
測試方法:
使用C語言中的庫函數(shù)測量程序開始到結(jié)束的時間,對各個優(yōu)化方法的時間進(jìn)行比較,而且我每次運(yùn)算后打印指定位置數(shù)據(jù),發(fā)現(xiàn)均相同,證明程序沒出錯。
測試結(jié)果:
????? ??
圖1性能測試截圖
從測試結(jié)果可以看到,我采用的優(yōu)化方法確實(shí)提高了程序性能,減少了程序運(yùn)行時間。
整理所有運(yùn)行時間如下表:
表1:性能測試結(jié)果
采用方法描述 |
運(yùn)行時間(s) |
(初始狀態(tài),沒有進(jìn)行任何優(yōu)化,局部性很差) |
0.86 |
after (一般有用的優(yōu)化,除法變?yōu)橐莆徊僮? |
0.55 |
after cache(空間局部性) |
0.78 |
after cpu(循環(huán)展開8) |
0.46 |
(整合所有非分塊的優(yōu)化方案) |
0.42 |
按列對矩陣進(jìn)行分塊(cache) |
0.80 |
按行對矩陣進(jìn)行分塊(cache) |
0.77 |
按列對矩陣進(jìn)行分塊+除法變成移位 |
0.52 |
按行對矩陣進(jìn)行分塊+除法變成移位 |
0.47 |
分成4*4的塊(cache) |
0.42 |
4.4 結(jié)合自己計算機(jī)的硬件參數(shù),分析優(yōu)化后程序的各個參數(shù)選擇依據(jù)。(15分)
我選擇結(jié)合自己計算機(jī)的硬件參數(shù),分析優(yōu)化后程序的各個參數(shù)選擇依據(jù)。
首先我查看了自己電腦的L1、L2、L3緩存大小,如下圖所示:
對分塊進(jìn)行優(yōu)化:
程序如下:
void fenkuai_cpu_row128(long img[1922][1082])
{
??? int bsize = 128;
??? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
??? int en = 1081; /* Amount that fits evenly into blocks */
??? int M = 1081, N = 1921;
? for (int jj = 1; jj < M; jj += bsize)
??? {
??? for (int ii = 1; ii < N; ii += bsize)
??????? {
??????????? for (int j = jj; j < ((jj + bsize) > M ? M : jj + bsize); j++)
??????????? {
??????????????? for (int i = ii; i < ((ii + bsize) > N ? N : ii + bsize); i++)
??????????????? {
??????????????????? img[i][j] = (img[i - 1][j] + img[i + 1][j] + img[i][j - 1] + img[i][j + 1]) / 4;
??????????????? }
??????????? }
??????? }
??? }
}???????????????
以下為程序運(yùn)行結(jié)果:
說明:Cache的層次,一般有L1, L2, L3 (L是level的意思)的cache。通常來說L1,L2是集成 在CPU里面的(可以稱之為On-chip cache),而L3是放在CPU外面(可以稱之為Off-chip cache)。當(dāng)然這個不是絕對的,不同CPU的做法可能會不太一樣。這里面應(yīng)該還需要加上 register,雖然register不是cache,但是把數(shù)據(jù)放到register里面是能夠提高性能的??梢允褂脺p少D-cache miss的數(shù)量,增加有效的數(shù)據(jù)訪問的數(shù)量。這個要比I-cache優(yōu)化難一些。由于我的L1高速緩存大小為512KB,8核,所以一個核為64KB,由于long數(shù)據(jù)類型是8個字節(jié)。每個分塊的大小為128*128, 所以每個塊的miss次數(shù)是128。這樣總的miss率就是1*1922*1081/(4*128),如果分成4*4的塊,不命中率就會很高,經(jīng)過實(shí)驗(yàn)分成更大的塊,實(shí)驗(yàn)效果不好,根據(jù)我電腦的參數(shù),選擇128*128的塊比選擇其他參數(shù)效果更好。
對循環(huán)展開優(yōu)化:
void success(long img[1922][1082])
{
? ? int i;
? ? long bef,aft;
? ? long B[1082];
? ? for (i = 0;i < 1082;i++) {
? ? ? ? B[i] = img[0][i];//瀛樺偍杈圭晫鍊?
? ? }
? ? for(int i=1;i<1921;i++)
? ? {
? ? ? ? for(int j=1;j<1081;j+=4)
? ? ? ? {
? ? ? ? ? ? bef=img[i][j+1];
? ? ? ? ? ? aft=img[i][j+2];
? ? ? ? ? ? img[i][j]=(B[j]+img[i+1][j]+img[i][j-1]+bef)/4;
? ? ? ? ? ? img[i][j+1]=(B[j+1]+img[i+1][j+1]+aft+img[i][j])/4;
? ? ? ? ? ? img[i][j+2]=(B[j+2]+img[i+1][j+2]+bef+img[i][j+3])/4;
? ? ? ? ? ? img[i][j+3]=(B[j+3]+img[i+1][j+3]+aft+img[i][j+4])/4;
? ? ? ? ? ? B[j] = img[i][j];
? ? ? ? ? ? B[j + 1] = img[i][j+1];
? ? ? ? ? ? B[j + 2] = img[i][j+2];
? ? ? ? ? ? B[j + 3] = img[i][j+3];
? ? ? ? }
? ? }
}
以下為運(yùn)行結(jié)果:
說明:一個規(guī)律應(yīng)當(dāng)是被普遍認(rèn)同的,那就是循環(huán)展開的程度越高,循環(huán)執(zhí)行開銷所占的比例就會越小??墒?,根據(jù)實(shí)驗(yàn)結(jié)果,循環(huán)展開4次的結(jié)果確實(shí)好于循環(huán)展開8次的結(jié)果,經(jīng)過分析,可能是由于循環(huán)展開8次初始化過多變量,導(dǎo)致程序性能提升效果比循環(huán)展開4次的效果差。
4.5 還可以采取的進(jìn)一步的優(yōu)化方案(5分)
正因?yàn)榫€程是調(diào)度的最小單位,控制好線程,也就相當(dāng)于控制好了計算資源。
多線程與異步計算的關(guān)系密切,一般可以使用異步計算的,都可以用多線程來實(shí)現(xiàn),而多線程加速也依賴于異步計算,利用多線程來并行處理多個任務(wù),提高計算資源的利用率,也可以對我們的程序進(jìn)行優(yōu)化,從而提升程序性能。
程序代碼如下,可以直接運(yùn)行調(diào)試:
#include "stdio.h"
#include<time.h>
#include "math.h"
long img[1922][1082];
// void timecmp(long img[1922][1082])
void test (long img[1922][1082])
{
??? // printf("\n");
??? for(int k=0;k<1900;k+=500)
??? {
??????? printf("%ld\t",img[k][1000]);
??? }
??? printf("\n");
}
void putong(long img[1922][1082])
{
??? for(int j = 1; j < 1081; j++)
??? {
??????? for (int i = 1; i < 1921;i++)
??????? {
??????????? /* code */
??????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) / 4;
??????? }
??? }
}
void cache1(long img[1922][1082])
{
??? for(int j = 1; j < 1081; j++)
??? {
??????? for (int i = 1; i < 1921;i++)
??????? {
??????????? /* code */
??????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) >> 2;
??????? }
??? }
}
void cache(long img[1922][1082])
{
??? for(int i = 1; i < 1921;i++)
??? {
??????? for (int j = 1; j < 1081; j++)
??????? {
??????????? /* code */
??????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) /4;
??????? }
??? }
}
void cpu(long img[1922][1082])//循環(huán)展開
{
??? int i;
??? long bef,aft;
??? long B[1082];
??? for (i = 0;i < 1082;i++) {
??????? B[i] = img[0][i];//存儲邊界值
??? }
??? for(int j=1;j<1080;j+=4)
??? {
??????? for(int i=1;i<1921;i++)
??????? {
??????????? bef=img[i][j+1];
??????????? aft=img[i][j+2];
??????????? img[i][j]=(B[j]+img[i+1][j]+img[i][j-1]+bef)/4;
??????????? img[i][j+1]=(B[j+1]+img[i+1][j+1]+aft+img[i][j])/4;
??????????? img[i][j+2]=(B[j+2]+img[i+1][j+2]+bef+img[i][j+3])/4;
??????????? img[i][j+3]=(B[j+3]+img[i+1][j+3]+aft+img[i][j+4])/4;
??????????? B[j] = img[i][j];
??????????? B[j + 1] = img[i][j+1];
??????????? B[j + 2] = img[i][j+2];
??????????? B[j + 3] = img[i][j+3];
??????? }
??? }
}
void fenkuai_cpu_row(long img[1922][1082])
{
??? int i, j, k, kk=4, jj=4;
??? int bsize = 4;
??? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
??? int en = 1921; /* Amount that fits evenly into blocks */
??? for (jj = 1; jj < en; jj += bsize)
??? {
????? ??for (i = jj; i < jj + bsize; i++)
??????? {
??????????? for (j = 1; j < 1081; j++)
??????????? {
??????????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) /4;
??????????? }
??????? }
??? }
}
void fenkuai_cpu_row_yi(long img[1922][1082])
{
??? int i, j, k, kk=4, jj=4;
??? int bsize = 4;
??? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
??? int en = 1921; /* Amount that fits evenly into blocks */
??? for (jj = 1; jj < en; jj += bsize)
??? {
??????? for (i = jj; i < jj + bsize; i++)
??????? {
??????????? for (j = 1; j < 1081; j++)
??????????? {
??????????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) >>2;
??????????? }
??????? }
??? }
}
void fenkuai_cpu_col(long img[1922][1082])
{
??? int i, j, k, kk=4, jj=4;
??? int bsize = 4;
??? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
??? int en = 1081; /* Amount that fits evenly into blocks */
??? for (jj = 1; jj < en; jj += bsize)
??? {
??????? for (i = 1; i < 1921; i++)
??????? {
??????????? for (j = jj; j < jj + bsize; j++)
??????????? {
??????????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) /4;
??????????? }
??????? }
??? }
}
void fenkuai_cpu_col_yi(long img[1922][1082])
{
??? int i, j, k, kk=4, jj=4;
??? int bsize = 4;
??? // int en = bsize * (1922/bsize); /* Amount that fits evenly into blocks */
??? int en = 1081; /* Amount that fits evenly into blocks */
??? for (jj = 1; jj < en; jj += bsize)
??? {
??????? for (i = 1; i < 1921; i++)
??????? {
??????????? for (j = jj; j < jj + bsize; j++)
??????????? {
??????????????? img[i][j] = (img[i-1][j] + img[i+1][j]+img[i][j-1] + img[i][j+1] ) >>2;
??????????? }
??????? }
??? }
}
void success(long img[1922][1082])
{
??? int i;
??? long bef,aft;
??? long B[1082];
??? for (i = 0;i < 1082;i++) {
??????? B[i] = img[0][i];//存儲邊界值
??? }
??? for(int i=1;i<1921;i++)
??? {
??????? for(int j=1;j<1081;j+=4)
??????? {
??????????? bef=img[i][j+1];
??????????? aft=img[i][j+2];
??????????? img[i][j]=(B[j]+img[i+1][j]+img[i][j-1]+bef)>>2;
??????????? img[i][j+1]=(B[j+1]+img[i+1][j+1]+aft+img[i][j])>>2;
??????????? img[i][j+2]=(B[j+2]+img[i+1][j+2]+bef+img[i][j+3])>>2;
??????????? img[i][j+3]=(B[j+3]+img[i+1][j+3]+aft+img[i][j+4])>>2;
??????? ????B[j] = img[i][j];
??????????? B[j + 1] = img[i][j+1];
??????????? B[j + 2] = img[i][j+2];
??????????? B[j + 3] = img[i][j+3];
??????? }
??? }
}
int main ()
{
??? printf("startle!\n");
??? int i = 0;
??? for(int i=0;i<1922;i++)
??? {
??????? for (int j = 0; j < 1082; j++)
??????? {
??????????? /* code */
??????????? img[i][j]= i+j;
??????? }
??? }
??? clock_t start_t = clock();
??? for(i=0;i<50;i++)
??????? putong(img);
??? clock_t end_t = clock();
??? test(img);
??? double sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("(初始狀態(tài),沒有進(jìn)行任何優(yōu)化,局部性很差)cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? cache1(img);
??? end_t = clock();
??? test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("after (一般有用的優(yōu)化,除法變?yōu)橐莆徊僮?cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? cache(img);
??? end_t = clock();
??? test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("after cache(空間局部性) cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? cpu(img);
??? end_t = clock();
??? test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("after cpu(循環(huán)展開) cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? success(img);
??? end_t = clock();
??? test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("(整合所有非分塊的優(yōu)化方案)cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? fenkuai_cpu_col(img);
??? end_t = clock();
??? test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("(分塊col)cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? fenkuai_cpu_row(img);
??? end_t = clock();
? ??test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("(分塊row)cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? fenkuai_cpu_col_yi(img);
??? end_t = clock();
??? test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
??? printf("(分塊col+移位)cost time: %f(s)\n",sum_time);
??? start_t = clock();
??? for(i=0;i<50;i++)
??????? fenkuai_cpu_row_yi(img);
??? end_t = clock();
??? test(img);
??? sum_time=((double)(end_t-start_t))/CLOCKS_PER_SEC;
?? ?printf("(分塊row+移位)cost time: %f(s)\n",sum_time);
}
第5章 總結(jié)
5.1 請總結(jié)本次實(shí)驗(yàn)的收獲
- 實(shí)驗(yàn)了很多優(yōu)化方案,對老師課上內(nèi)容理解更透徹
- 解決啦以前對程序性能的疑惑,即為什么時間復(fù)雜度高的程序運(yùn)行有時候更快
- 掌握時間函數(shù)使用方法
- 提升數(shù)學(xué)思維,鍛煉編程能力
- 了解了圖像平滑算法
- 總的來說,這個實(shí)驗(yàn)收獲還是很多的。尤其是對miss次數(shù)的定量分析,讓我很受益。之前學(xué)習(xí)算法之類的,只會大概估計一下復(fù)雜度的等級,完全定量地對程序分析對我來說還是比較少。在其他方面,如怎樣寫出對緩存友好的代碼,也有不少收獲。
5.2 請給出對本次實(shí)驗(yàn)內(nèi)容的建議
實(shí)驗(yàn)內(nèi)容很好,就是某些問題描述不清,例如4.4題,沒有給出操作實(shí)例,可能導(dǎo)致很多學(xué)生不知道從何做起,希望能改進(jìn)這一點(diǎn),其余做的都很好。
注:本章為酌情加分項(xiàng)。
參考文獻(xiàn)
為完成本次實(shí)驗(yàn)?zāi)惴喌臅c網(wǎng)站等
[1]? 林來興. 空間控制技術(shù)[M]. 北京:中國宇航出版社,1992:25-42.
[2]? 辛希孟. 信息技術(shù)與信息服務(wù)國際研討會論文集:A集[C]. 北京:中國科學(xué)出版社,1999.
[3]? 趙耀東. 新時代的工業(yè)工程師[M/OL]. 臺北:天下文化出版社,1998 [1998-09-26]. http://www.ie.nthu.edu.tw/info/ie.newie.htm(Big5).
[4]? 諶穎. 空間交會控制理論與方法研究[D]. 哈爾濱:哈爾濱工業(yè)大學(xué),1992:8-13.
[5]? KANAMORI H. Shaking Without Quaking[J]. Science,1998,279(5359):2063-2064.文章來源:http://www.zghlxwxcb.cn/news/detail-423831.html
[6]? CHRISTINE M. Plant Physiology: Plant Biology in the Genome Era[J/OL]. Science,1998,281:331-332[1998-09-23]. http://www.sciencemag.org/cgi/ collection/anatmorp.文章來源地址http://www.zghlxwxcb.cn/news/detail-423831.html
到了這里,關(guān)于哈工大csapp-LAB3程序優(yōu)化的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!