如何避免過早優化的詛咒
已發表: 2022-03-11這幾乎是值得保證的,真的。 從新手到專家,從架構到 ASM,以及從機器性能到開發人員性能的任何優化,您和您的團隊很有可能會縮短自己的目標。
什麼? 我? 我的團隊?
這是一個相當嚴重的指控。 讓我解釋。
優化不是聖杯,但它可能同樣難以獲得。 我想與您分享一些簡單的技巧(以及一大堆陷阱),以幫助您將團隊的體驗從自我破壞的體驗轉變為和諧、滿足、平衡以及最終優化的體驗。
什麼是過早優化?
過早的優化試圖優化性能:
- 首次編寫算法時
- 在基準確認之前,您需要
- 在分析精確點之前需要優化的地方
- 比您的項目當前要求的級別低
現在,我是一個樂觀主義者,擎天柱。
至少,在我寫這篇文章的時候,我會假裝自己是一個樂觀主義者。 就你而言,你可以假裝你的名字是擎天柱,這樣它會更直接地對你說話。
作為技術人員,您有時可能想知道它怎麼可能是$year ,然而,儘管我們取得了所有進步,但對於$task來說,如此令人討厭的耗時卻是可以接受的標準。 你想變瘦。 高效的。 驚人的。 像 Rockstar Programmers 這樣的人,這些職位都在叫囂著,但有領導者的印章。 因此,當您的團隊編寫代碼時,您鼓勵他們第一次就做對(即使“正確”在這裡是一個高度相關的術語)。 他們知道這是 Clever Coder 的方式,也是那些以後不需要浪費時間重構的人的方式。
我覺得。 完美主義的力量有時在我體內也很強大。 您希望您的團隊現在花一點時間來節省以後的大量時間,因為每個人都在努力完成他們分享的“其他人寫的爛代碼(他們到底在想什麼?)”。 簡稱 SCOPWWHWTT,因為我知道你喜歡不發音的首字母縮略詞。
我也知道您不希望您的團隊的代碼為他們自己或其他任何人使用。
因此,讓我們看看可以做些什麼來引導您的團隊朝著正確的方向前進。
優化什麼:歡迎來到這是一門藝術
首先,當我們想到程序優化時,我們通常會立即假設我們正在談論性能。 即使這已經比看起來更模糊(速度?內存使用情況?等等)所以讓我們停在那裡。
讓我們讓它更加模棱兩可! 剛開始。
我的蜘蛛網般的大腦喜歡在可能的情況下創造秩序,所以我需要每一盎司的樂觀情緒來考慮我要說的是一件好事。
有一個簡單的(性能)優化規則,那就是不要這樣做。 這聽起來很容易嚴格遵循,但並非所有人都同意。 我也不完全同意。 有些人會簡單地寫出比其他人更好的代碼。 希望對於任何給定的人來說,他們在全新項目中編寫的代碼質量通常會隨著時間的推移而提高。 但我知道,對於許多程序員來說,情況並非如此,因為他們知道的越多,他們就越容易過早地進行優化。
對於許多程序員來說……他們知道的越多,他們就越容易過早地進行優化。
所以這個不要做它不能是一門精確的科學,而只是為了抵消典型的技術人員解決難題的內在衝動。 畢竟,這首先是吸引許多程序員參與這項技術的原因。 我明白了。 但請他們保存它,以抵制誘惑。 如果你現在需要一個解謎的出口,你總是可以涉足週日報紙的數獨遊戲,或者拿起一本門薩書,或者去打高爾夫球打一些人為的問題。 但是在適當的時間之前將其排除在回購之外。 幾乎總是這比預優化更明智。
請記住,這種做法臭名昭著,以至於人們會問過早的優化是否是萬惡之源。 (我不會走那麼遠,但我同意這種觀點。)
我並不是說我們應該在每個設計級別選擇我們能想到的最腦殘的方式。 當然不是。 但是,我們可以考慮其他值,而不是選擇看起來最聰明的值:
- 最容易向新員工解釋
- 最有可能通過最有經驗的開發人員的代碼審查
- 最可維護的
- 最快的寫
- 最容易測試
- 最便攜
- 等等。
但這就是問題本身很困難的地方。 這不僅僅是避免優化速度、代碼大小、內存佔用、靈活性或面向未來的能力。 這是關於平衡以及你所做的是否真的符合你的價值觀和目標。 它完全是上下文相關的,有時甚至無法客觀地衡量。
為什麼這是一件好事? 因為生活就是這樣。 很亂。 我們面向編程的大腦有時非常想在混亂中創造秩序,以至於我們最終諷刺地加劇了混亂。 這就像試圖強迫某人愛你的悖論。 如果你認為你已經成功了,那就不再是愛; 與此同時,你被指控劫持人質,你可能比以往任何時候都需要更多的愛,而這個比喻一定是我能選擇的最尷尬的比喻之一。
不管怎樣,如果你認為你已經為某事找到了完美的系統,那麼……我想,在它持續的時候享受幻覺吧。 沒關係,失敗是學習的絕佳機會。
牢記用戶體驗
讓我們探討一下用戶體驗如何融入這些潛在的優先事項。 畢竟,在某種程度上,甚至想要表現良好的東西也是關於 UX 的。
如果您正在處理 UI,無論代碼使用什麼框架或語言,都會有一定數量的樣板和重複。 嘗試減少這種情況在程序員時間和代碼清晰度方面絕對是有價值的。 為了幫助平衡優先級的藝術,我想分享幾個故事。
在一份工作中,我工作的公司使用了一個基於自以為是的技術堆棧的閉源企業系統。 事實上,賣給我們的供應商如此固執己見,拒絕進行不符合堆棧意見的 UI 定制,因為這對他們的開發人員來說太痛苦了。 我從來沒有使用過他們的堆棧,所以我不會因此而譴責他們,但事實是這種“對程序員有利,對用戶不利”的權衡在某些情況下對我的同事來說太麻煩了,以至於我結束了編寫第三方插件來重新實現系統 UI 的這一部分。 (這是一個巨大的生產力助推器。我的同事喜歡它!十多年後,它仍然為每個人節省了時間和挫敗感。)
我並不是說意見本身就是一個問題。 在我們的案例中,太多的問題成為了問題。 舉個反例,Ruby on Rails 的一大吸引力恰恰在於它是固執己見的,在前端生態系統中,人們很容易因為有太多選擇而感到眩暈。 (給我一些有意見的東西,直到我弄清楚我自己的!)
相反,您可能會想在您的項目中將 UX 冠以一切之王。 一個有價值的目標,但讓我講述我的第二個故事。
在上述項目成功幾年後,我的一位同事來找我,要求我通過自動化有時會出現的某些混亂的現實生活場景來優化用戶體驗,以便只需單擊一下即可解決。 我開始分析是否有可能設計一個不會有任何誤報或誤報的算法,因為該場景有許多奇怪的邊緣情況。 我與同事談論得越多,我就越意識到這些要求根本不會得到回報。 這種情況只是偶爾出現一次——比如說每月一次——目前需要一個人幾分鐘來解決。 即使我們能夠成功地實現自動化,並且沒有任何錯誤,所需的開發和維護時間也需要幾個世紀才能得到回報,因為我的同事節省了時間。 我內心的討人喜歡的人很難說“不”,但我不得不縮短談話時間。
因此,讓計算機盡其所能幫助用戶,但僅限於理智的範圍內。 你怎麼知道那是什麼程度呢?
我喜歡採用的一種方法是像開發人員分析他們的代碼一樣分析用戶體驗。 從您的用戶那裡找出他們花費最多時間點擊或一遍又一遍地輸入相同內容的地方,看看您是否可以優化這些交互。 您的代碼能否對他們最有可能輸入的內容做出一些有根據的猜測,並將其設為無輸入默認值? 除了某些禁止的上下文(無點擊 EULA 確認?)之外,這確實會影響用戶的工作效率和幸福感。 如果可以的話,做一些走廊可用性測試。 有時,您可能難以解釋計算機可以輕鬆提供哪些幫助,哪些不方便……但總體而言,此值可能對您的用戶非常重要。
避免過早優化:何時以及如何優化
儘管我們對其他環境進行了探索,但現在讓我們明確假設我們正在為本文的其餘部分優化原始機器性能的某些方面。 我建議的方法也適用於其他目標,比如靈活性,但每個目標都有自己的陷阱; 要點是過早地優化任何東西都可能會失敗。

那麼,在性能方面,究竟有哪些優化方法可以遵循呢? 讓我們深入挖掘。
這不是草根倡議,這是三重
TL;DR 是:從頂部向下工作。 更高級別的優化可以在項目的早期進行,較低級別的應該留到以後。 這就是獲得“過早優化”這一短語的大部分含義所需的全部內容; 不按此順序做事很可能會浪費團隊的時間並適得其反。 畢竟,您不會從一開始就用機器代碼編寫整個項目,對嗎? 所以我們的AAA作案手法是按這個順序優化:
- 建築學
- 算法
- 集會
普遍的看法是,算法和數據結構通常是優化最有效的地方,至少在性能方面是這樣。 但請記住,架構有時會決定可以使用哪些算法和數據結構。
我曾經發現一個做財務報告的軟件,它通過為每筆金融交易多次查詢 SQL 數據庫,然後在客戶端進行非常基本的計算。 使用該軟件的小企業只用了幾個月,就連他們相對較少的財務數據也意味著,有了全新的台式機和相當強大的服務器,報告生成時間已經達到了幾分鐘,這是他們需要經常使用的一種。 最後我寫了一個簡單的 SQL 語句,其中包含求和邏輯——通過將工作轉移到服務器來避免所有重複和網絡往返——甚至幾年後的數據,我的版本可以生成在相同的舊硬件上只需幾毫秒即可獲得相同的報告。
有時您對項目的架構沒有影響力,因為在項目中為時已晚,架構更改不可行。 有時您的開發人員可以像我在上面的示例中那樣繞過它。 但是,如果您剛開始一個項目並對其架構有一定的發言權,那麼現在是優化它的時候了。
建築學
在一個項目中,架構是事後更改成本最高的部分,所以這是一個可以在開始時進行優化的地方。 例如,如果您的應用程序要通過鴕鳥傳遞數據,您將希望將其構建為低頻、高負載數據包,以避免使瓶頸變得更糟。 在這種情況下,您最好擁有俄羅斯方塊的完整實現來娛樂您的用戶,因為加載微調器不會削減它。 (開個玩笑:幾年前,我正在安裝我的第一個 Linux 發行版 Corel Linux 2.0,我很高興長時間運行的安裝過程包括了這一點。看過 Windows 95 安裝程序的電視廣告屏幕很多次,我已經記住了它們,這當時是呼吸新鮮空氣。)
作為架構更改成本高昂的一個示例,上述 SQL 報告首先如此高度不可擴展的原因從其歷史中可以清楚地看出。 該應用程序隨著時間的推移而發展,從其起源於 MS-DOS 和一個甚至最初不是多用戶的本土定制數據庫。 當供應商最終切換到 SQL 時,模式和報告代碼似乎已經一對一地移植了。 每當他們通過實際利用 SQL 對給定報告的優勢來完成架構切換時,這讓他們在整個更新過程中獲得了令人印象深刻的 1,000% 以上的性能改進。 適合像我當時的雇主這樣的鎖定客戶的業務,並且顯然試圖在初始過渡期間優先考慮編碼效率。 但在某些情況下,滿足客戶的需求就像錘子擰螺絲一樣有效。
架構部分是關於預測您的項目需要能夠擴展的程度以及以何種方式擴展。 因為架構是如此高級,如果不將我們的關注點縮小到特定的技術和領域,就很難將我們的“注意事項”具體化。
我不會這樣稱呼它,但其他人都會這樣做
值得慶幸的是,互聯網上充斥著關於大多數曾經夢想過的建築的集合智慧。 當您知道是時候優化您的架構時,研究陷阱幾乎可以歸結為找出描述您出色願景的流行語。 很有可能有人和你有同樣的想法,嘗試過,失敗過,迭代過,並在博客或書中發表過。
僅通過搜索來識別流行詞可能會很棘手,因為對於您所謂的 FLDSMDFR,其他人已經創造了 SCOPWWHWTT 一詞,他們描述了您正在解決的相同問題,但使用的詞彙與您完全不同。 開發者社區來救援! 盡可能詳細地描述 StackExchange 或 HashNode,加上所有你的架構不是的流行語,這樣他們就知道你做了足夠的初步研究。 有人會很樂意開導你。
同時,一些一般性的建議可能是值得深思的。
算法和組裝
給定一個有利的架構,這裡是您團隊中的編碼人員將在他們的時間裡獲得最多 T-bling 的地方。 過早優化的基本避免也適用於此,但您的程序員最好考慮此級別的一些細節。 當涉及到實現細節時,需要考慮的事情太多了,以至於我寫了一篇關於代碼優化的單獨文章,專門針對一線和高級編碼人員。
但是,一旦您和您的團隊實施了一些未優化的性能方面的事情,您是否真的將其保留為Don't do it ? 你從不優化?
你說得對。 下一條規則是,僅限專家,不要這樣做。
是時候進行基準測試了!
您的代碼有效。 也許它太慢了,以至於您已經知道需要優化,因為它是經常運行的代碼。 也許你不確定,或者你有一個 O(n) 算法並且認為它可能沒問題。 無論如何,如果這個算法值得優化,我現在的建議是一樣的:運行一個簡單的基準測試。
為什麼? 不是很清楚我的 O(n³) 算法不可能比其他任何東西都差嗎? 嗯,有兩個原因:
- 您可以將基準添加到您的測試套件中,作為您性能目標的客觀衡量標準,無論它們當前是否得到滿足。
- 即使是專家也會不經意間讓事情變慢。 即使看起來很明顯。 真的很明顯。
不相信我的第二點?
如何從 1,400 美元的硬件中獲得比 7,000 美元的硬件更好的結果
StackOverflow 名聲的 Jeff Atwood 曾指出,有時(在他看來,通常情況下)購買更好的硬件比將寶貴的程序員時間花在優化上更划算。 好的,所以假設您已經得出了一個相當客觀的結論,即您的項目適合這種情況。 讓我們進一步假設您要優化的是編譯時間,因為這是您正在處理的一個龐大的 Swift 項目,這已成為一個相當大的開發人員瓶頸。 硬件購物時間!
你應該買什麼? 嗯,顯然,日元對日元,更昂貴的硬件往往比更便宜的硬件表現更好。 很明顯,一台 7,000 美元的 Mac Pro 編譯軟件的速度應該比一些中檔 Mac Mini 快,對吧?
錯誤的!
事實證明,有時更多的內核意味著更高效的編譯……在這種特殊情況下,LinkedIn 發現他們的堆棧情況正好相反。
但我看到管理層犯了一個更大的錯誤:他們甚至沒有在前後進行基準測試,並且發現硬件升級並沒有讓他們的軟件“感覺”更快。 但是沒有辦法確定; 此外,他們仍然不知道瓶頸在哪裡,所以他們對性能仍然不滿意,因為他們已經用完了他們願意分配給問題的時間和金錢。
好的,我已經進行了基準測試。 我真的可以優化嗎?
是的,假設您已經決定需要這樣做。 但也許該決定將等到更多/所有其他算法也被實施,因此您可以通過分析查看移動部件如何組合在一起以及哪些最重要。 這可能是針對小型應用程序的應用程序級別,也可能僅適用於一個子系統。 無論哪種方式,請記住,一個特定的算法對整個應用程序來說似乎很重要,但即使是專家——尤其是專家——也容易誤診。
三思而後行
“我不知道你們這些人,但是……”
作為最後的思考點,請考慮如何將錯誤優化的想法應用於更廣泛的觀點:您的項目或公司本身,甚至是經濟部門。
我知道,人們很容易認為技術將拯救這一天,我們可以成為實現這一目標的英雄。
另外,如果我們不這樣做,其他人就會這樣做。
但請記住,儘管有最好的意圖,權力也會腐敗。 我不會在這裡鏈接到任何特定的文章,但如果你沒有瀏覽過任何文章,那麼值得尋找一些關於擾亂經濟的更廣泛影響,以及這有時最終會為誰服務。 您可能會對嘗試通過優化來拯救世界的一些副作用感到驚訝。
後記
你注意到什麼了嗎,擎天柱? 我唯一一次叫你擎天柱是在開始和現在結束的時候。 你在整篇文章中都沒有被稱為擎天柱。 老實說,我忘記了。 我寫了整篇文章,沒有叫你擎天柱。 最後,當我意識到我應該回去把你的名字灑在整個文本中時,我內心的一個小聲音說,不要這樣做。
