程式區域性性原理感悟

2022-07-04 10:36:10 字數 4199 閱讀 7887

區域性性原理

程式的區域性性原理是指程式在執行時呈現出區域性性規律,即在一段時間內,整個程式的執行僅限於程式中的某一部分。相應地,執行所訪問的儲存空間也侷限於某個記憶體區域。

區域性性原理又表現為:時間區域性性和空間區域性性。

時間區域性性是指如果程式中的某條指令一旦執行,則不久之後該指令可能再次被執行;如果某資料被訪問,則不久之後該資料可能再次被訪問。

空間區域性性是指一旦程式訪問了某個儲存單元,則不久之後。其附近的儲存單元也將被訪問。

這一規律是是普遍事實的總結,更是許多計算機技術的前提假設,比如.net中託管堆以及代齡的處理過程,便是基於這個認識。

之所以有這個規律,很多人認為原因是:程式的指令大部分時間是順序執行的,而且程式的集合,如陣列等各種資料結構是連續存放的。對於這一點,我個人表示贊同。

程式的區域性性原理是如此重要,以至於與程式設計的各個方面都存在密切的關係。

區域性性與效率

熟悉**的區域性性原理,並且按照這個思路去寫**,可以顯著的提高**的執行效率,先看下面的c#**:

static

void main(string

args)

} }); //

按照先列後行的順序遍歷二維陣列,這是異常做法

writetimes(() =>

} });

console.read();}

static

void

writetimes(action func)

102

999

這個例子本身並沒什麼實際的意義,但如果處理的資料量足夠大,並且可能需要頻繁的在外存、記憶體、快取間排程,又注重效率的話,這個問題就有可能會被陡然放大了。不過,通常來說,效率總是在程式出現效能問題後才應該被關注的方面

區域性性與快取

//

寫法一:迴圈內塞進好幾件事

for (int i = 0; i < 1000; i++)

//寫法二:迴圈內只幹一件事

for (int i = 0; i < 1000; i++)

for (int i = 0; i < 1000; i++)

問:兩種寫法哪個好?

有的同學認為寫法一效率高,因為迴圈只執行了一遍,而有的同學認為寫法二效率高,因為該寫法中每個迴圈內的區域性變數大部分情況下是比寫法一少,這樣更容易利用cpu的暫存器以及各級快取,這滿足區域性性原理,所以效率較好。

我寫了簡單的程式驗證了一下,發現確實有時候寫法一執行時間較短,有時候寫法二執行之間較短,沒有明顯的固定規律,所以我認為這裡的效率一說不太明顯,當然了也許是這裡的迴圈次數比較少,迴圈多次的低效還沒有體現出來,感興趣的可以自己試一下大的迴圈。

即使是這樣的結果,我還是傾向於使用第二種寫法,這不是效率的原因,而是重構中,提倡乙個迴圈只幹一件事。

區域性性與重構

重構的基本原理這裡就不多說了,感興趣的隨便搜一下就可以了。重構的基本原則中就有諸如:乙個迴圈只幹好一件事,關聯性強的**放到一起,變數定義在使用的地方等等。這些原則與區域性性原理闡述的規律竟然是如出一轍。

看一些我認可的寫法:

//

乙個迴圈內只幹好一件事

for (int i = 0; i < 1000; i++)

for (int i = 0; i < 1000; i++) //

原始的**

list salarylist = new list();

list

levellist = new list();

list

scorelist = new list();

collecthighsalary(salarylist);

collecthighlevel(levellist);

collecthighscore(scorelist);

collectmiddlesalary(salarylist);

collectmiddlelevel(levellist);

collectlowsalary(salarylist);

collectlowlevel(levellist);

//重構成:

//有關係的**放到一起

//變數需要時再定義

list salarylist = new list();

collecthighsalary(salarylist);

collectmiddlesalary(salarylist);

collectlowsalary(salarylist);

list

levellist = new list();

collecthighlevel(levellist);

collectmiddlelevel(levellist);

collectlowlevel(levellist);

list

scorelist = new list();

collecthighscore(scorelist);

區域性性原理不僅與語句和函式的組織方式息息相關,還與元件的組織方式互相呼應。

區域性性與高內聚

從元素(函式,物件,元件,乃至服務)設計的角度,內聚性是描述乙個元素的成員之間關聯性強弱尺度。如果乙個元素具有很多緊密相關的成員,而且它們有機的結合在一起去完成有限的相關功能,那這個元素通常就是高度內聚的。高內聚的設計是一種良好的設計。

耦合性從另乙個角度描述了元素之間的關聯性強弱。元素之間聯絡越緊密,其耦合性就越強,元素的獨立性則越差,元素間耦合的高低取決於元素間介面的複雜性,呼叫的方式以及傳遞的資訊。低耦合的設計是一種良好的設計。

乙個具有低內聚,高耦合的元素會執行許多互不相關的邏輯,或者完成太多的功能,這樣的元素難於理解、難於重用、難於維護,常常導致系統脆弱,常常受到變化帶來的困擾。

毫無疑問,遵循良好的區域性性原理通常能得到良好的高內聚低耦合元素,反之,**中元素的高內聚低耦合也使的區域性性得以大大加強,此所謂相得益彰。

區域性性與命名

說到命名,不得不提著名的匈牙利命名法。

在我讀了《軟體隨想錄:程式設計師部落酋長joel談軟體》一書之前,我認為的匈牙利命名法則就是在駝峰式命名的基礎上,在變數名前加上變數的型別,例如ilength表示int型的表示長度的變數。

在該書中,作者將匈牙利命名法分為兩種,流行的並且被廢掉的叫「系統型匈牙利命名法則」,這種命名法將變數型別加到了變數名字前面,老實說確實沒什麼意義,特別是在現代編輯器中。

事實上,微軟那位仁兄推薦的是叫做「應用型匈牙利命名法則」的規則,那就是把變數的應用場景加到變數的名字前面

比如在頁面開發中,直接從使用者輸入得到的name字串可以起名叫:usname,其中us代表unsafe,意思是這個字串是使用者輸入的,沒用經過編碼處理,可能是不安全的。而經過編碼的name字串可以起名叫sname,其中s代表safe,意思是這個字串經過了編碼處理,是安全的。

**錯誤檢查也是乙個經典的話題,如何讓**的錯誤提前暴露出來,而不是發布後由客戶去發現,這是個問題。

滿足區域性性原理,使得我們的程式內聚性通常很好,但是毫無疑問,有些元素還是必須要貫穿很多的行的,比如在某些函式中,定義變數和末次使用變數的地方可能相差幾十行:

var usname =getname();

action1(usname);

//此處省略20行...

sname =usname;

//此處省略10行...

document.write(sname);

我不得不承認,joel老兄提出的「應用型匈牙利命名法則」還是相當有作用的。比如中間那行:

sname = usname;

我們很容易就會從變數名發現這行**存在安全性威脅。

我們其實生活在世界的區域性中

推而廣之,區域性性原理不僅僅是適用於程式的理論,而是適用於我們生活的各個方面的重要規律,它的稱呼向來隨著場合的不同而有所變化,比如有時叫「習慣」,有時叫「慣性」,有時又演化成「熟悉的人/事」等。總之,我個人認為,人總是傾向於在區域性的、連續的時間空間內做相關的、熟悉的事情,程式其實是人類做事風格的反應。

區域性性原理

區域性性原理 cpu訪問 儲存器時,無論是訪問指令還是訪問資料,所訪問的 儲存單元 都趨於聚集在乙個較小的連續區域中。三種不同型別的區域性性 時間區域性性 temporal locality 如果乙個資訊項正在被訪問,那麼在近期它很可能還會被再次訪問。程式迴圈 堆疊等是產生時間區域性性的原因。順序區...

區域性性原理

區域性性通常有兩種不同的形式 時間區域性性和空間區域性性。時間區域性性 在乙個具有良好的時間區域性性的程式中,被訪問過一次的儲存器位置很可能在不遠的將來會被再次訪問。空間區域性性 在乙個具有良好空間區域性性的程式中,如果乙個儲存器位置被訪問了一次,那麼程式很可能在不遠的將來訪問附近的乙個儲存器位置。...

區域性性原理

平常在服務端軟體開發中,通常會把資料儲存在資料庫裡,服務端遇到的效能瓶頸往往發生在訪問資料庫的時候,在資料庫前通過redis加資料快取是常見的效能優化方式。如何判定新增快取的策略一定是有效的呢?不同的儲存器之間,訪問速度 和容量都有幾十乃至上千倍的差異。在效能和 的巨大差異,能不能既享受cpu ca...