區域性性原理
程式的區域性性原理是指程式在執行時呈現出區域性性規律,即在一段時間內,整個程式的執行僅限於程式中的某一部分。相應地,執行所訪問的儲存空間也侷限於某個記憶體區域。
區域性性原理又表現為:時間區域性性和空間區域性性。
時間區域性性是指如果程式中的某條指令一旦執行,則不久之後該指令可能再次被執行;如果某資料被訪問,則不久之後該資料可能再次被訪問。
空間區域性性是指一旦程式訪問了某個儲存單元,則不久之後。其附近的儲存單元也將被訪問。
這一規律是是普遍事實的總結,更是許多計算機技術的前提假設,比如.net中託管堆以及代齡的處理過程,便是基於這個認識。
之所以有這個規律,很多人認為原因是:程式的指令大部分時間是順序執行的,而且程式的集合,如陣列等各種資料結構是連續存放的。對於這一點,我個人表示贊同。
程式的區域性性原理是如此重要,以至於與程式設計的各個方面都存在密切的關係。
區域性性與效率
熟悉**的區域性性原理,並且按照這個思路去寫**,可以顯著的提高**的執行效率,先看下面的c#**:
staticvoid main(string
args)
} }); //
按照先列後行的順序遍歷二維陣列,這是異常做法
writetimes(() =>
} });
console.read();}
static
void
writetimes(action func)
102999
這個例子本身並沒什麼實際的意義,但如果處理的資料量足夠大,並且可能需要頻繁的在外存、記憶體、快取間排程,又注重效率的話,這個問題就有可能會被陡然放大了。不過,通常來說,效率總是在程式出現效能問題後才應該被關注的方面。
區域性性與快取
//寫法一:迴圈內塞進好幾件事
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...