代碼高效性和健壯性的權衡

來源:互聯網
上載者:User
代碼高效性和健壯性的權衡

http://www.cnblogs.com/walzer/archive/2009/12/14/1623945.html

Posted on 2009-12-14 17:29
Walzer 閱讀(428) 評論(0)
編輯 收藏

這個是比較早, 09年4月份的事情了。整理文檔翻出來,覺得還有點意思.

 

當時CLIENT-SERVER的通訊封包格式有兩種方案

a. 以7E為開頭和結尾, PAYLOAD中所有7E的位元組, 都在其後擴充一個BYTE, 寫為7E, 7D, (稱為轉義). 封包中不帶CHECKSUM, CRC等校正用的欄位

b. 以7E為開頭和結尾, 帶一個CHECKSUM欄位, PAYLOAD中不進行7E->7E 7D的轉義.

幾個同事就這個通訊封包格式, 採用方案一或方案二, 開會激烈討論了個把小時。
我原先反對轉義方案的出發點比較模糊, 只是覺得原先轉義的方案"不優雅"; 後來才想清楚了不優雅的"本質"在哪裡.

 

所有的代碼, 可以在抽象意義上分作兩大塊, 兩者的著重點是不同的.

(1) 正常啟動並執行代碼. 首要追求高效性,
    這個"高效性"如果從邏輯的角度來解釋, 那麼一方面是"高效"地對正確的資料執行正確的演算法(方法/策略), 另一方面是"高效"地找出異常, 然後丟給異常處理代碼去處理.

(2) 處理異常的代碼. 首要追求健壯性. 
    就是程式必須能從異常中自我恢複. 由於代碼多數時間跑的是"正常"邏輯, 少數情況下才不得不處理"異常", 所以"異常"處理的代碼中, 首要任務是健壯, 跑不死, 而高效性則是次要的.

 

那麼回到轉義的策略上來看,原先的7E -> 7E 7D, 使得裝包和拆包的時候, 時間上都必須挨位元組掃描過去, 空間上必須另開一塊記憶體, 這些"不優雅"的工作是為了應對網路傳輸時包資料丟失. 包資料丟失是一個異常情況,而轉義策略本質上就是不論好包壞包,一棍子打死, 統統要經過轉義演算法. 用上面的觀點解釋, 即"為了異常情況下的健壯性,犧牲了正常情況下的高效性".

而用Header + Length + CheckSum + Payload + Tailer的做法, 邏輯上是這樣的

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->if ( 以Length為基礎, 得知CheckSum正確 和 Tailer正確){    正確的包,走正常處理流程, 直接把Payload傳給上層邏輯處理} else{    錯誤的包,走異常處理流程,挨位元組掃描下一個Header, 然後再算length, checksum, tailer等}

 

這是在上面"正常->高效性 & 異常->健壯性"指導思想下的做法. 那麼現在就剩最後一個問題, 計算checksum和轉義的工作相比, 哪一個更快? 如果轉義處理的效率, 比checksum更高,那麼上面的假設就不成立了.

所以我做了個實驗, 代碼如下

代碼 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->BYTE* pBuf1 = new BYTE[1024];BYTE* pBuf2 = new BYTE[1024*2];UINT8 sum = 0;DWORD tStart = 0, tEnd = 0;// CODE 1, 轉義處理tStart = GetTickCount();  // WM上的毫秒級時間for(int j = 0; j < 100000; j++){    for(int i1 = 0, i2 = 0; i1 < 1024; i1++, i2++)    {        if (pBuf1[i1] == 0x7E && pBuf1[i1+1] == 0x7D)        {            pBuf2[i2] = pBuf1[i1];            i1++;                    }         else        {            pBuf2[i2] = pBuf1[i1];        }    }tEnd = GetTickCount();        printf("copy 1024 bytes * 100K times, use %d ms\n", tEnd - tStart);// CODE 2, CHECK SUMtStart = GetTickCount();for(int j = 0; j< 100000; j++){    for(int i = 0; i < 1024; i++)    {        sum += pBuf1[i];    }}tEnd = GetTickCount();printf("check sum 1024 bytes *100K times,  use %d ms\n", tEnd - tStart);

 

上面這段代碼,在SAMSUNG 2442 400MHz的CPU, WM 6.1系統上運行結果是

copy 1024 bytes * 100K times, use 11677 ms
check sum 1024 bytes *100K times,  use 7504 ms

所以, 一個正確的資料包, 經過CHECKSUM計算的時間, 比其經過轉義計算的時間要快得多, 僅為其64%.  這是手機上的情況, 伺服器上的百分比不太清楚是什麼樣,但至少有一點是肯定的,就是用CHECKSUM的方案比用轉義的方案,在正常邏輯情況下速度更快、記憶體開銷更少。當伺服器同時處理十萬數量級網路資料包的時候, 效能提升還是比較可觀的。

 

這篇文章的重點不在於哪個方案更嚴謹,或者上面的邏輯對不對,而是在於這麼一個思想:

(1) 正常啟動並執行代碼. 首要追求高效性,

(2) 處理異常的代碼. 首要追求健壯性. 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.