效能永遠不是優先考慮的問題

needrunning發表於2019-05-14

SAE部落格上看到一篇文章,摘錄有價值的段落存檔,收藏。

我從來不會一開始就考慮效能問題。如果專案成本很低,甚至到專案結束時,如果沒有感覺到明顯的效能問題,也不會去管。要知道現在已經不是DOS的年代,CPU的計算能力很高,但成本很低了。重要一點是,如果只針對提升效能對程式碼做改動,很容易破壞程式碼的複用性和可維護性。而返過來,提高了程式碼的複用性和可維護性,則很容易提高效能。

下面有一個PHP的程式碼例項,功能是幫助使用者重置密碼(程式碼為了簡單說明問題,請不要太在意一些無關的細節)requestResetPassword是接收使用者重置密碼的請求並且做了相應的檢查。為了更好的複用性,我將重置密碼的操作單獨分配到一個新的resetPassword的函式,更改完密碼的後再呼叫sendEmail向使用者傳送一封通知郵件。

/**
 * 使用者請求重置密碼的接收器
 */
function requestResetPassword() {
    //檢查使用者是否存在
    if( !checkUserExists( $_GET[`userid`] ) ) {
        exit(`抱歉,使用者不存在,請確認使用者帳號。`);
    }
    resetPassword( $_GET[`userid`] );
    //最後向使用者傳送一封郵件
    sendEmail( $_GET[`userid`], `重置密碼成功`, `新的密碼是xxxx` );
    exit(`新密碼已經傳送到你的郵箱。`);
}
 
/**
 * 幫助使用者重置密碼
 */
function resetPassword( $userid ) {
    //檢查使用者是否存在
    if( !checkUserExists( $userid ) ) {
        return false;
    }
 
    //進行重置使用者密碼的操作
    //略...
    return true;
}
 
/**
 * 向使用者傳送一封郵件
 */
function sendEmail( $userid, $title, $content ) {
    //檢查使用者是否存在
    if( !checkUserExists( $userid ) ) {
        return false;
    }
 
    //傳送郵件操作
    //略...
    return true;
}
 
/**
 * 檢查某個使用者是否存在
 */
function checkUserExists( $userid ) {
    $user = getUserInfo( $userid );
    return !empty( $user );
}
 
/**
 * 獲取某個使用者的資料
 */
function getUserInfo( $userid ) {
    //假設我有一個query的函式,它用來查詢資料庫並返回資料
    $user = query( "SELECT * FROM `user` WHERE `uid`=" . intval( $userid ) );
    return is_array( $user ) ? $user : array() ;
}

  現在問題是,這三個函式都同時使用checkUserExists這個函式來檢查使用者不存在,資料庫查詢了三次,這樣帶來了一些額外的開銷。

如果要去掉三者之間任意一個checkUserExists,看上去是可能的。但是如果之後有某些功能要呼叫resetPassword或者sendEmail,使用者不存在時,系統可能會發生錯誤。

還有一個解決方法是,將resetPassword的邏輯寫到requestResetPassword裡,再過一點,把sendEmail的邏輯也寫進去。這樣函式呼叫減少,資料庫查詢也變成一次了,效能得到了提高。但是重置密碼和傳送郵件的功能將不能得到複用,並且違背了單一責任的原則,程式碼複雜度也提高了。

不過,因為函式分離和複用性都很好,如果實際效能受到影響,可能考慮用快取的方法減少資料庫查詢,我改動了它們共用的checkUserExists函式:

/**
 * 檢查某個使用者是否存在
 */
function checkUserExists( $userid ) {
    //增加一個快取,用以記錄檢查使用者的結果
    static $cache = array();
 
    //檢查當前使用者是否已經檢查過一次
    if( isset( $cache[ $userid ] ) ) {
        return $cache[ $userid ];
    }
 
    $user = getUserInfo( $userid );
    //把結果記錄到快取中
    $cache[ $userid ] = !empty( $user );
 
    return $cache[ $userid ];
}

  

也可以用同樣的方法改動getUserInfo函式。

這裡可以看到,當程式碼的複用性提高時,想提高效能是很簡單的,效能的瓶頸也很容易被發現和修改。

儘管這個例子對效能影響還不夠大,還有一些影響更大的,比如說遍歷,我可能為了複用而將遍歷封裝到一個函式中,並且多次使用它。這些開銷對我的專案根本沒有預想中那樣有太大的影響,或者說是微乎其微的。所以我更願意把時間花在如何提高程式碼的複用性和維護性方面,而不是糾結於浪費多這一點效能。實際效能如果真的達不到要求,也可以權衡增加硬體配置。


 個人經驗

我是很同意文中觀點的,比如平時在專案中會經常使用foreach遍歷list陣列,陣列長度一般不會超過幾十個,這樣只是理論上對效能有影響,實際根本感覺不到。

我還是期待開發出有更多人使用的產品,那時再調整優化效能就顯得意義更大。用簡約的技術實現最大的效益


 甘於平凡

程式設計師真的很高傲,在我接觸過的人中,包括我自己也是。我以前經常對一些簡單的程式碼感到不屑,而總想在專案中寫一些犀利的程式碼,讓人看起來很NB,但結果總是和想象差太遠,程式碼總是寫的很差,邏輯也不夠清晰。歸根到底,是我帶著這樣的思想去寫程式碼,而忽略了程式設計的根本:解決問題。現在我改掉了這個壞毛病,以解決問題為目的去程式設計以簡單為主。出乎意料的是別人有時會對我說,這裡的程式碼寫得很棒。

踏實的做事,會有意想不到的收穫。

參考:

http://blog.sae.sina.com.cn/archives/657

http://www.php.net/manual/zh/function.is-array.php

http://cn2.php.net/manual/zh/function.exit.php


相關文章