<listing id="lnlbz"></listing>

      <address id="lnlbz"></address>
      <form id="lnlbz"><th id="lnlbz"><listing id="lnlbz"></listing></th></form>

          <form id="lnlbz"></form>

          <progress id="lnlbz"><nobr id="lnlbz"></nobr></progress>

          <address id="lnlbz"><sub id="lnlbz"><menuitem id="lnlbz"></menuitem></sub></address><listing id="lnlbz"><font id="lnlbz"><cite id="lnlbz"></cite></font></listing><thead id="lnlbz"></thead><rp id="lnlbz"></rp>

          1. 移動端
            訪問手機端
            官微
            訪問官微

            搜索
            取消
            溫馨提示:
            敬愛的用戶,您的瀏覽器版本過低,會導致頁面瀏覽異常,建議您升級瀏覽器版本或更換其他瀏覽器打開。

            Go代碼重構:23倍性能提升!

            來源:csdn 2018-07-13 09:35:16 代碼 重構 金融安全
                 來源:csdn     2018-07-13 09:35:16

            核心提示要說寫代碼是每位程序員的使命,那么寫優秀的代碼則是每位程序員的底線。本文作者分享基于Go語言的代碼重構,使得性能提升23倍的快速方法。

              要說寫代碼是每位程序員的使命,那么寫優秀的代碼則是每位程序員的底線。本文作者分享基于 Go 語言的代碼重構,使得性能提升 23 倍的快速方法。

              以下為譯文:

              幾周前,我讀了一篇名為“Go 語言中的好代碼與差代碼”(https://medium.com/@teivah/good-code-vs-bad-code-in-golang-84cb3c5da49d)的文章,作者一步步地向我們介紹了一個實際業務用例的重構。

              文章的主旨是利用 Go 語言的特性將“差代碼”轉換成“好代碼”,即更加符合慣例和更易讀的代碼。但是它也堅持性能是項目重要的方面。這就引起了我探索的好奇心:讓我們深入看看!

              這篇文章里的程序基本上就是讀取輸入文件,然后解析每一行并存儲到內存的對象中。

            Go代碼重構:23倍性能提升!

              作者不僅在 Github 上發布了他的代碼(https://github.com/teivah/golang-good-code-bad-code),還寫了個性能測試程序。這真是個好主意,鼓勵大家調整代碼并用如下命令重現測量結果:

            Go代碼重構:23倍性能提升!

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好)

              基于此,在我的機器上測出“好代碼”速度提升了 16%。那么我們可以進一步提高嗎?

              以我的經驗看來,代碼質量和性能間的相互關系非常有趣。如果你成功地重構代碼,讓代碼更清晰,更進一步分離,那么最終代碼速度會加快,因為它不會像以前一樣徒勞無功地執行不相關的指令,而且一些可能的優化會凸顯出來,且易于實現。

              另一方面,如果進一步追求性能,那就不得不放棄簡單性并訴諸黑科技。實際上你只減少了幾毫秒,但是代碼的質量會受到影響,會變得晦澀難懂、脆弱且缺乏靈活性。

            Go代碼重構:23倍性能提升!

            簡單性先是上升,繼而下降

              你需要權衡利弊:應該進行到什么程度?

              為了正確地確定性能的優先級,最有價值的策略是找到瓶頸,然后集中精力改善??梢允褂梅治龉ぞ邅碜?!例如 Pprof(https://blog.golang.org/profiling-go-programs) 和 Trace(https://making.pusher.com/go-tool-trace/):

            Go代碼重構:23倍性能提升!

            Go代碼重構:23倍性能提升!

            一個非常大CPU使用圖

            Go代碼重構:23倍性能提升!

            Go代碼重構:23倍性能提升!

            彩虹追蹤:許多小任務

              追蹤結果證明所有的 CPU 內核都得到了利用,乍一看似乎不錯。但是它顯示了幾千個很小的彩色計算片段,還有一些空白表示內核閑置。讓我們放大一點:

            Go代碼重構:23倍性能提升!

            3毫秒的窗口

              實際上,每個內核都有大量閑置的時間,并且在多個微型任務間不斷切換??雌饋砣蝿盏牧6炔⒉焕硐?,從而導致大量上下文切換,還有同步引起的資源爭搶。

              我們用數據沖突檢測器檢查下同步是否正確(如果同步都不正確,那問題就不只是性能了):

            Go代碼重構:23倍性能提升!

              很好!看起來沒問題,沒有遇到數據沖突。

              “好代碼”中的并發策略是把輸入中的每一行交給單獨的 Go 例程,以便利用多核。這是合理的直覺,因為 Go 例程以輕量和廉價著稱。那么并發能帶來多少好處呢?讓我們比較一下使用單一 Go 例程順序執行的代碼(僅需在調用行解析函數的時候,刪掉關鍵字go)。

            Go代碼重構:23倍性能提升!

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好)

              哎呀,實際上不用并行的代碼速度更快。這意味著啟動go例程的開銷超過了同時使用多核所節省的時間。

              現在我們放棄并發,轉而使用順序執行,那么下一步自然是不要使用通道來傳遞結果,以節省開銷。我們用一個裸分片來代替。

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好)

              僅僅通過簡化代碼,刪除并發,現在“好代碼”版本將速度提高了40%。

            Go代碼重構:23倍性能提升!

            使用單個go例程的時候,一段時間內僅有1個CUP在工作

              現在讓我們看看Pprof圖形都調用了哪個函數。

            Go代碼重構:23倍性能提升!

            找到瓶頸

              我們目前的版本的狀況是:86%的時間真正用在了解析消息上,這非常好。我們立刻注意到43%的時間用在了匹配正則表達式上:調用(*Regexp).FindAll。

              雖然從原始文本中抽取數據時,正則表達式非常方便,而且很靈活,但是它們也有弊端,例如需要耗費內存和運行時間。正則表達式很強大,但是在很多情況下是殺雞用牛刀。

              在我們的程序中,文本模式為:

            Go代碼重構:23倍性能提升!

              主要是為了識別以“-”開頭的“命令”,而且一行可能有多個命令。我們可以用bytes.Split做一些略微的調整。讓我們用Split替換代碼中的正則表達式:

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好)

              哇,這一改速度又提高了40%!

              現在 CPU 的圖如下所示:

            Go代碼重構:23倍性能提升!

              沒有正則表達式的巨大開銷了。5個不同的函數中的內存分配占用了40%的時間,還說得過去。很有意思的是現在21%的時間被bytes.Trim占據了。

            Go代碼重構:23倍性能提升!

            這個函數調用讓我很感興趣:我們可以改善它嗎?

              bytes.Trim需要一個“cutset string”作為參數(用于分隔符),但我們的分隔符只是一個空格而已。這就是個可以引入一些復雜性來提高性能的例子:實現自己定義的“trim”函數來代替標準庫。自定義的“trim”僅處理單個分隔符字節。

            Go代碼重構:23倍性能提升!

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好)

              哈哈,又快了20%。目前的版本的速度是最初“差代碼”的4倍,雖然我們只用到了機器的一個CPU內核。相當可觀!

              早些時候,我們在處理每行輸入的級別放棄了并發,但是我們仍然可以在更粗的力度上使用并發提高性能。 例如,如果每個文件在各自的go例程中進行處理,那么在我的工作站上處理6千個文件(6千個消息)的速度要比串行更快:

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好,紫色代表并發

              速度提高了66%(也就是提到了3倍),看起來不錯,但是想到它使用了我所有12個CPU內核,那么這個結果“也沒有那么好”。這可能意味著,使用新的優化代碼,處理單個文件仍然是一項“小任務”,go例程和同步的開銷不可忽略。

              有趣的是,如果將消息數量從6千增加到12萬,對于串行版本的性能沒有影響,而且還會降低“每個消息1個例程”版本的性能。這是因為啟動大量go例程是可能的,有時也很有用,但它確實給go的運行時間調度帶來了一些壓力。

              我們可以通過僅創建幾個工作進程(例如12個持續運行的go例程)來進一步縮短執行時間(雖然達不到12倍,但還是會加快速度),每個go例程處理消息的一個子集:

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好,紫色代表并發)

              與串行版本相比,針對大量消息進行改進后的并發減少了79%的執行時間。 請注意,只有在確實需要處理大量文件時,此策略才有意義。

              最佳地利用所有CPU內核的代碼由幾個go例程組成,每個go例程負責處理一定量的數據,在處理完成之前不進行任何通信和同步。

              一種常見的啟發式方法就是選擇與可用CPU核心數量相等的進程(go例程),但它并不總是最佳選擇,因為每個任務的情況都不一樣。 例如,如果任務是從文件系統讀取數據或發出網絡請求,那么從性能的角度來看,go例程多于CPU核心數量是完全正確的。

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好,紫色代表并發)

              現在,解析代碼的效率很難再通過局部改進來提高了。執行時間中的主要部分是小對象的分配和垃圾回收(例如消息結構),這是合理的,因為我們知道內存管理操作相對較慢。 對分配策略的進一步優化......權當是留給高手們的一個練習吧。

              使用完全不同的算法也會可以大幅提高速度。

              這時,我從 Rob Pike 的《Lexical Scanning in Go》演講中獲得了靈感。構建自定義語法分析其和自定義解析器。 這只是一個原型(我沒有實現所有的極端情況),它不如原始算法直觀,并且正確實現錯誤處理可能會很棘手。 但是,它的速度比前一個版本提高了30%。

            Go代碼重構:23倍性能提升!

            每次執行所需的微秒數(越小越好,紫色代表并發)

              好了,與最初的代碼相比,速度提高了 23 倍。

              今天就說這么多,我希望你們能喜歡這篇文章。下面是一些免責聲明和建議的關鍵點:

              ·在許多抽象的層次上都可以通過不同的技巧提高性能,以獲得性能的成倍增長。

              ·首先在最高抽象層次上調優:數據結構,算法,以及正確的解耦合。低層調優放在后面:輸入輸出,批處理,并發,標準庫的使用,內存管理等。

              ·算法復雜度分析十分重要,但并不是讓程序運行得更快的最佳工具。

              ·性能測試很難。通過分析工具和性能測試發現瓶頸,以獲得代碼的執行情況。時刻牢記性能測試不是最終用戶在生產環境中感受到的“真正”延遲,所以性能測試數據僅供參考。

              ·幸運的是,工具(Bench、Pprof、Trace、數據沖突檢測器、Cover)使得檢查性能變得十分容易,并且鼓舞人心。

              ·停下來問問自己,多快才算快。不要浪費時間去優化一次性的腳本。要記住,優化也是要付出成本的:工程時間、復雜度、bug,還有技術債務。

              ·混淆代碼之前一定要慎重!

              ·Ω(n2) 以及更高的算法通常都很昂貴。

              ·復雜度在O(n)或O(n log n)及以下的算法一般都沒問題。

              ·隱藏因素不能忽略!例如,本文中的所有改進都是針對隱藏因素的,而沒有改變算法的復雜度級別。

              ·輸入輸出通常都是瓶頸,如網絡請求、數據庫查詢、文件系統訪問等。

              ·正則表達式的代價通常會超過實際需要。

              ·內存分配比計算更昂貴。

              ·棧中的對象比堆中的對象代價更低。

              ·分片可以用來替代昂貴的內存重新分配。

              ·字符串在只讀的情況下很合適(包括重新分片),但對于其他一切操作,[]byte的效率更高。

              ·內存的局部性很重要(更適合CPU緩存)。

              ·并發和并行很有用,但很難用好。

              ·在深入到更底層時會遇到你不希望在Go語言中解決的“玻璃地板”。如果你開始使用匯編指令、intrinsic函數、SIMD指令……或許你應該考慮用Go語言做原型,然后換成低級語言來榨干硬件性能,節省每一納秒!

            1024你懂的国产日韩欧美_亚洲欧美色一区二区三区_久久五月丁香合缴情网_99爱之精品网站

            <listing id="lnlbz"></listing>

                <address id="lnlbz"></address>
                <form id="lnlbz"><th id="lnlbz"><listing id="lnlbz"></listing></th></form>

                    <form id="lnlbz"></form>

                    <progress id="lnlbz"><nobr id="lnlbz"></nobr></progress>

                    <address id="lnlbz"><sub id="lnlbz"><menuitem id="lnlbz"></menuitem></sub></address><listing id="lnlbz"><font id="lnlbz"><cite id="lnlbz"></cite></font></listing><thead id="lnlbz"></thead><rp id="lnlbz"></rp>

                      責任編輯:韓希宇

                      免責聲明:

                      中國電子銀行網發布的專欄、投稿以及征文相關文章,其文字、圖片、視頻均來源于作者投稿或轉載自相關作品方;如涉及未經許可使用作品的問題,請您優先聯系我們(聯系郵箱:cebnet@cfca.com.cn,電話:400-880-9888),我們會第一時間核實,謝謝配合。

                      為你推薦

                      猜你喜歡

                      收藏成功

                      確定