本文旨在幫助大家降低在編碼過程中寫出低性能和耗內(nèi)存的概率,只要大家在寫代碼時稍注意下,積少成多。當然,使用代碼分析工具也可以調(diào)試優(yōu)化代碼的性能,它可以分析出代碼中性能和內(nèi)存消耗的問題,如果這些地方可以重構(gòu),那么代碼也可以得到改進。但在沒有分析工具的情況下,可視化代碼檢查是必需。
1. 循環(huán)
循環(huán)的性能是由以下因素決定:
- 循環(huán)內(nèi)進行的工作;
- 在循環(huán)中進行的檢查,以確定它是否可以退出;
在循環(huán)內(nèi)進行的工作應該保持在最低限度,并且對循環(huán)邊界進行的檢查應該具有最低的開銷。典型例子如下:
例子1:在循環(huán)開始之前將變量設(shè)置為數(shù)組的大小,可以節(jié)省每次迭代時計算a.size()的開銷。
// Lower Performance Version
int a[];
int total = 0;
for(int i = 0; i < a.size(); i++) begin
total += a[i];
end
// Higher Performance Version
int a[];
int a_size;
int total = 0;
a_size = a.size();
for(int i = 0; i < a_size; i++) begin
total += a[i];
end
例子2:在循環(huán)中搜索數(shù)組的特定值,一旦找到就可以終止循環(huán)了。
// Lower Performance Version
int a[50];
int find = -1;
// Look up an index via the array:
foreach(a[i]) begin
if(a[i] == 20) begin
find = i;
end
end
// Higher Performance Version
int a[50];
int find = -1;
// Look up an index via the array:
foreach(a[i]) begin
if(a[i] == 20) begin
find = i;
break;
end
end
例子3:在每次循環(huán)迭代時查找關(guān)聯(lián)數(shù)組中的值是不必要的,因為它可以在循環(huán)開始時查找。另外在小數(shù)組方面,foreach()循環(huán)結(jié)構(gòu)通常比for(int i = 0;i
// Lower Performance Version
int a[];
int b[string];
foreach ( a[i] ) begin
total += a[i] * b["yes"];
end
// Higher Performance Version
int a[];
int b[string];
int t = b["yes"];
foreach ( a[i] ) begin
total += a[i] * t;
end
2. 決策
在邏輯或算術(shù)基礎(chǔ)上做決策時,有許多優(yōu)化可以幫助提高性能。
一旦發(fā)現(xiàn)邏輯表達式的一個元素為假,就放棄對它的求值,它可以加快決策的速度,因此邏輯表達式中各個運算數(shù)的排序可以避免不必要的性能開銷,如下例子:
例子1:對于AND求值,如果表達式的第一項為假,則跳過求值的其余部分。
if(A && B && C) begin
// do something
end
例子2:對于OR求值,如果表達式的第一項為真,則跳過求值的其余部分。
if(A || B || C) begin
// do something
end
例子3:如果表達式中的各項具有不同級別的性能開銷,則應該對這些項進行排序,首先計算開銷最小的項。
// Lower Performance Version
if((B.size() > 0) && B[$] == 42 && A) begin
// do something
end
// Higher Performance Version
if(A && (B.size() > 0) && B[$] == 42) begin
// do something
end
優(yōu)化算術(shù)運算也可以實現(xiàn)優(yōu)化,這不僅適用于決策邏輯,也適用于計算變量。如下例子避免了一個乘法操作。
// Lower Performance Version
if(((A*B) - (A*C)) > E) begin
// do something
end
// Higher Performance Version
if((A*(B - C)) > E) begin
// do something
end
如果知道決策樹中條件的相對頻率,則將最頻繁出現(xiàn)的條件移到樹的頂部,這最常應用于case語句和嵌套if語句。
例子1:大多數(shù)情況下,case語句在一次檢查后退出,節(jié)省了進一步的比較。
// Lower Performance Version
// Case options follow the natural order:
case(char_state)
START_BIT: // do_something to start tracking the char (once per word)
TRANS_BIT: // do something to follow the char bit value (many times per word)
PARITY_BIT: // Check parity (once per word, optional)
STOP_BIT: // Check stop bit (once per word)
endcase
// Higher Performance Version
// case options follow order of likely occurrence:
case(char_state)
TRANS_BIT: // do something to follow the char bit value (many times per word)
START_BIT: // do_something to start tracking the char (once per word)
STOP_BIT: // Check stop bit (once per word)
PARITY_BIT: // Check parity (once per word, optional)
endcase
例子2:如果ready無效,則不會計算其余代碼,然后進行read_cycle檢查,這樣就不需要進行write_cycle檢查了。
// Lower Performance Version
// ready is not valid most of the time
// read cycles predominate
if(write_cycle) begin
if(addr inside {[2000:10000]}) begin
if(ready) begin
// do something
end
end
end
else if(read_cycle) begin
if(ready) begin
// do something
end
end
// Higher Performance Version
// ready is not valid most of the time
// read cycles predominate
if(ready) begin
if(read_cycle) begin
// do something
end
else begin
if(addr inside {[2000:10000]}) begin
// do something
end
end
end
3. 方法調(diào)用
task和function統(tǒng)稱為method(方法)。在某些情況下,重構(gòu)調(diào)用方法的代碼會更好,以便將方法的內(nèi)容展開直接放到代碼中,而不是使用方法調(diào)用。如果方法相對較短并且有多個參數(shù),那更可以提升性能了。
在Systemverilog中,通過在task或function調(diào)用開始時復制變量,然后將方法執(zhí)行期間所做的任何更改結(jié)果復制回去,來完成向task和function傳遞參數(shù)的功能。如果參數(shù)是復雜的變量類型(如字符串或數(shù)組),那么這可能會造成相當大的開銷,而另一種選擇是使用引用(ref)。使用ref節(jié)省了參數(shù)傳遞拷貝的開銷,如果變量在任務或函數(shù)中被更新,則它不會被復制到函數(shù)中,因此它也會在調(diào)用方法中被更新。如果不想讓它被更新,可以讓變量為const ref,這將使其稱為只讀引用的。例子如下,在性能較低的代碼版本中,將一個int類型隊列和一個字符串復制到函數(shù)中。隨著隊列長度的增長,將會影響到更多的性能和內(nèi)存。而在高性能版本中,int類型隊列和string參數(shù)都是引用,這避免了復制操作并加快了函數(shù)的執(zhí)行速度。
// Lower Performance Version
function void do_it(input int q[$], input string name);
// ...
endfunction: do_it
// Higher Performance Version
function void do_it(ref int q[$], ref string name);
// ...
endfunction: do_it
4. 數(shù)組
SystemVerilog有許多具有不同特征的數(shù)據(jù)類型,在使用時需要考慮哪種類型的數(shù)組最適合。如下列出了這些數(shù)組的特性。

例如,使用關(guān)聯(lián)數(shù)組而不是使用靜態(tài)數(shù)組對只有稀疏條目的大型內(nèi)存空間進行建模可能更有效。但是,如果關(guān)聯(lián)數(shù)組由于條目的數(shù)量而變得很大,那么使用固定數(shù)組來建模內(nèi)存空間將變得更有效。
在關(guān)聯(lián)數(shù)組的使用中,可能會使用未添加到數(shù)組的索引進行訪問,例如稀疏內(nèi)存。當關(guān)聯(lián)數(shù)組獲得超出范圍的訪問權(quán)限時,默認情況下它將返回一條警告消息以及一個未初始化的值。為了避免這種情況,可以查詢數(shù)組以確定索引是否存在,如果不存在,則不進行訪問。如果使用默認變量語法,那么可以通過性能改進來避免這項工作,例子如下:
// Lower Performance Version
// Associative array declaration - no default value:
int aa[int];
if(aa.exists(i)) begin
lookup = aa[i];
end
// Higher Performance Version
// Associative array declaration - setting the default to 0
int aa[int] = {default:0};
lookup = aa[i];
5. 類
在Systemverilog中,類封裝了屬性(數(shù)據(jù)變量)和對這些屬性進行操作的方法。可以擴展類以添加更多變量,并添加或擴展現(xiàn)有方法以提供新功能。所有這些便利和功能帶來了性能開銷,可以通過以下方法來優(yōu)化。
盡量減少創(chuàng)建對象的數(shù)量,因為每構(gòu)造一個對象可能會有一個與之相關(guān)的開銷。如下優(yōu)化例子:
// Lower Performance Version
function chi_class get(chi_state_t chi_state);
chi_class chi_txn = new();
if ( chi_state == ON ) begin
// ...
end
return null;
endfunction : get
// Higher Performance Version
function chi_class get(chi_state_t chi_state);
chi_class chi_txn;
if ( chi_state == ON ) begin
chi_txn = new();
// ...
end
return chi_txn;
endfunction : get
直接對變量賦值比調(diào)用set()/get()方法更快,調(diào)用方法來更新或檢查變量比通過類層次路徑直接訪問帶來更高的開銷。普通的OOP準則建議類中的數(shù)據(jù)變量只能通過方法訪問。使用直接類層次路徑訪問變量可以提高性能,但可能會降低代碼的可重用性,并且依賴于用戶知道所討論的變量的名稱和類型的假設(shè)。具體情況需要具體分析。
class name;
int A;
function void set_A(int value);
A = value;
endfunction: set_A
endclass : name
// Lower Performance Version
name m = new();
m.set_A(1);
// Higher Performance Version
name m = new();
m.A = 1;
另外在類中調(diào)用方法會帶來開銷,嵌套或?qū)⒎椒ㄕ{(diào)用鏈接在一起也會增加開銷,在實現(xiàn)或擴展類時,盡量減少所涉及的方法嵌套層次。
6. 隨機約束
隨機約束生成是SystemVerilog中最強大的功能之一。在類中編寫受約束的隨機代碼時,需要考慮以下幾點:
- 除非必須,要盡量減少隨機函數(shù)的調(diào)用;
- 盡量減少rand變量的數(shù)量,如果一個值可以從其它隨機變量中計算出來,那么它不應該被定義為rand;
- 使用最小的數(shù)據(jù)類型,比如能用bit就不用logic,并將數(shù)據(jù)位寬調(diào)整到所需要的最小值;
- 使用分層類結(jié)構(gòu)來減少隨機化;
- 避免在約束中使用算術(shù)運算符;
- 隱含運算符是雙向的,使用solve before來強制前項的概率分布;
- 使用pre_randomize()方法預先設(shè)置或預先計算隨機化過程中使用的狀態(tài)變量;
- 使用post_randomize()方法來計算依賴于隨機變量的變量值;
7. 覆蓋率
Covergroup基本上是一組計數(shù)器,當采樣值與bin匹配時,計數(shù)器會增加,覆蓋率提升性能的方法是盡可能使用covergroup。Covergroup的基本規(guī)則是管理采樣bins的創(chuàng)建和covergroup的采樣。
每個coverpoint自動轉(zhuǎn)換為一組bins或計數(shù)器,用于在覆蓋點中采樣的變量的每個可能值。這相當于2n個bins,其中n是變量中的位數(shù),但這通常被SystemVerilog auto_bins_max變量限制為最多64個bins。在寫covergroup時要仔細分析,在目的達到的基礎(chǔ)上,盡量減少創(chuàng)建bins的數(shù)目,有助于提供性能。另外,在控制采樣時刻上要盡量在期望的行為發(fā)生時去調(diào)用sample,而不是在每個時鐘沿。
-
編碼器
+關(guān)注
關(guān)注
45文章
4006瀏覽量
143222 -
計數(shù)器
+關(guān)注
關(guān)注
32文章
2320瀏覽量
98473 -
運算器
+關(guān)注
關(guān)注
1文章
164瀏覽量
17026 -
Verilog語言
+關(guān)注
關(guān)注
0文章
113瀏覽量
8822
發(fā)布評論請先 登錄
何為高質(zhì)量的代碼?如何寫出高質(zhì)量代碼?
如何寫出可以讓人理解的代碼(以verilog語言為例)?
round robin 的 systemverilog 代碼
如何寫出高效并且簡潔易于閱讀的單片機C語言代碼呢?
如何寫出讓CPU執(zhí)行更快的代碼?
一本教你怎么寫出讓同事無法維護的代碼
教你如何寫ADC代碼,輸出電壓,讀取光敏值。
C語言如何寫出高效代碼呢?
如何寫出穩(wěn)定的單片機代碼
教你如何寫出性能更高的SystemVerilog代碼
評論