1. 程式人生 > >(轉載)效能調優--永遠超乎想象 (原文最終修訂於 2006-08-28 晚上11:48:38)

(轉載)效能調優--永遠超乎想象 (原文最終修訂於 2006-08-28 晚上11:48:38)

未經博主同意轉載此部落格,如此好的部落格,後生只是看著心喜,便未經同意轉載,希望各位去原部落格閱讀:http://blog.csdn.net/rmartin/article/details/1132312

多年以前,我在開發一個C++的應用程式。我的同伴Jim Newkirk(當時的)過來告訴說,我們的一個公用函式執行得非常的緩慢。這個函式是用來轉換二進位制的樹結構資料為普通文字,並存儲到檔案中的。(這是在XML出現之前,但概念類似於XML)

我審視了這個函式一會兒,發現了一個線性查詢演算法,於是毫無疑問的將這個線性查詢演算法替換為二分查詢法(譯註:binary search),然後就把這個函式交回給了Jim。Jim幾小時後就回來了,問我是否有做過任何的改進,因為這個函式還是遲緩如初。

看來是我沒找到關鍵,於是就一遍又一遍的研究了這個函式,然後發現並改進了一些其它很明顯的演算法問題,可是效能依然沒有絲毫改進。函式依然緩慢,看到我對這個函式的無計可施,Jim也越來越沮喪。

最後,Jim終於找到了一個能夠去分析這個函式效能的辦法,就發現這個問題出自一個底層的叫strstream的C++庫(譯註1)。這個庫函式隨著文字資料的不斷增加,它不停的一次又一次的申請記憶體塊。這個函式單純根據即將讀入的文字資料大小來預先申請記憶體塊,速度也迅速的隨之以數量級的趨勢降低。

很久以前,曾有一次我要寫一個計算任意多邊形面積的演算法。我想出了一個不斷的把這個任意多邊形細分為三角形的主意。每次細分一個三角形,多邊形就會減少一個頂點,而它的面積就可以由此累加起來。由於不得不處理很多不規則的形狀,好久我才把這個功能寫好。一、兩天後,我就完成了這個厲害的演算法,它能計算任意的多邊形的面積。

幾天後,我的一個同事過來找我,說:“我新畫了多邊形的一條邊,可它花了45分鐘才計算出面積。所以,我要是重新繪製這條線斷或是調整它,面積都顯示不出。”45分鐘啊,很長的時間了,所以我就問她這個多邊形有多少個頂點,她告訴我有超過1,000個的。

看了看演算法,我認識到這個演算法的複雜度是O(N^3)的(譯註2),所以對於小多邊形來說很快,但對於大型的來說速度就慢得無法忍受了。我一遍又一遍的思考著這個問題,但卻找不到一個更好的演算法。(現今我們只要用google搜尋就好了,可那是現在而這是那時...)於是我們就把這個自動顯示面積的功能去掉,然後告訴客戶這太耗時了。

兩週之後,純屬偶然機會, 我正翻閱一本關於prolog程式語言的書(一個可愛又另類的程式語言,我建議你也學習學習它),然後就發現了一個計算多邊形面積的演算法。它優雅,簡單,而且是線性階(譯註2)

的,我是從來都沒寫出過這麼漂亮的演算法。我用了幾分鐘時間就實現了它,哇!即使拖動多邊形一個頂點繞著螢幕亂轉,面積竟然也可以及時更新。

 昨天晚上,我坐在一輛豪華轎車裡,從O'Hare駛向我在芝加哥北部郊區的家。I-294公路正在施工,而我們湊好趕上交通阻塞。於是我拿出我的Macbook Pro,然後開始即興的編寫Ruby程式。為了好玩,我開始編寫埃拉托色尼質數過濾演算法(譯註:Sieve of Eratosthenes)。我想讓程式一跑起來就能看到Ruby有多快,所以就在程式中增加了benchmark模組來度量速度。它相當快!能在兩秒鐘內算出所有在百萬以內的素數!對於一個解釋型語言來說還不錯。

我想知道這個演算法的複雜度O(x)是什麼樣的。坐在車裡不好算出來,於是我決定通過一些設定點取樣的方法來測出它。我從100,000開始到5,000,000,每間隔100,000執行一次這個演算法取樣,然後把這些取樣點繪在了一個圖上。竟然是線性階!

這個演算法怎能是線性階呢?它有一個巢狀的迴圈!難道不應該是複雜度類似於O(N^2),或者至少也應該是O(N log N)啊?這裡就是程式碼,你自己來看看:

 require 'benchmark'
def sievePerformance(n)
  r = Benchmark.realtime() do 
    sieve = Array.new(n,true)
    sieve[0..1] = [false,false]
    
    2.upto(n) do |i|
      if sieve[i] 
        (2*i).step(n,i) do |j|
          sieve[j] = false
        end
      end
    end
  end
  r
end

我的兒子Micah就坐在我旁邊,他看了看然後說:“這個迴圈最多隻應做到n平方根。”我慚愧的意識到這一定是導致線性階的原因。這個迴圈本應該在它剛到n平方根的時候就結束了的,可卻陷入了無用的線性迭代之中一直到n。

這個簡單的改變應該不僅僅能在取樣圖表上展現出原本的曲線形狀,演算法的效能也應能提高不少。如下:

require 'benchmark'
def sievePerformance(n)
  r = Benchmark.realtime() do 
    sieve = Array.new(n,true)
    sieve[0..1] = [false,false]
    
    2.upto(Integer(Math.sqrt(n)) do |i|
      if sieve[i] 
        (2*i).step(n,i) do |j|
          sieve[j] = false
        end
      end
    end
  end
  r

我把這兩幅取樣圖表拼接在了一起,如下:

真令人失望。首先,在圖上的sqrt(n)沒有展現出曲線來;其次,sqrt(n)的效能僅僅是原先的兩倍!一個函式的外迴圈上限在指數級別上變為的一半(即原來的平方根),可是速度的提升卻怎能只有2倍?

隨著我對這個演算法理解的加深,我認識到外層迴圈的迭代次數的增加,內層迴圈所耗用時間會因為兩個因素而減少。首先,步長增大了;其次,在篩選過程中出現了更多的'false'值,因此判斷語句會更少頻率的被執行。這兩個導致時間耗用降低的因素一定是導致演算法保持線性階的某種平衡因素。

我不是電腦科學家,而且對鑑別這個演算法到底是線性與否的數學問題我也不是非常感興趣。誰能猜出當外部迴圈的範圍縮小到原來上限值的平方根而效能卻只有2倍增長的原因?誰能猜到演算法本身竟然是線性階的?!

六年前,當大家剛開始沉迷於XP的時候,Kent Beck(譯註3)要在一組學生(大概30個左右)前示意一個演算法,我就為他寫了這個埃拉托色尼質數過濾演算法的Java例程。我驚訝的看到他從函式中把n的平方根刪掉,並替換成了n。他說“我不知道這是不是真的能讓演算法加快,不管如何,把上限設為n使得可讀性更好。”於是,他刪掉了這個特別的註釋,那是我在平方根周圍註釋來解釋為何不把上限設為n的聰明之處。

那時我眼珠子亂轉而且還在一旁偷偷傻笑。我確信,如果n很大的時候,上限是n平方根會讓演算法的效率在數量級上大於n的,我還深信n每擴大一百倍,它所耗費的時間只會隨之增加大概十倍。六年後(昨晚),我終於知道了程式的結果,而且知道了增幅是2倍線性階的,而對此Kent一直都是對的。

譯註:

1,strstream,標準C/C++的字串流類,派生自iostream。因效能問題,C++標準委員會做了修補,用stringstream替換之,因此也不建議再使用strstream。

2,複雜度(本文指時間複雜度),以演算法中基本運算的重複執行次數作為演算法時間複雜度的時間量度,並以符號O(x)來表示。通常,時間複雜度由小到大分為幾個等級,a)常量階 O(c),b)對數階 O(log2n),c)線性階 O(n),d)多項式階 O(nm)等

3,Kent Beck,是軟體開發方法學的泰斗、XP的創始人,長期致力於軟體工程的理論研究和實踐,並具有講授XP的豐富經驗。作為軟體業內最富創造,哇和最有口碑的領導人之一,KentBeck極力推崇模式、極限程式設計和測試驅動開發。他現在加盟於ThreeRivers研究所,是多部暢銷書如《Smalltalk Best PracticePatterns》、《解析極限程式設計——擁抱變化》和《規劃極限程式設計》(和Martin Fowler合著)的作者,並且是超級暢銷書《重構——改善既有程式碼的設計》(中國電力出版社出版中英文版)的特約撰稿人。

作者簡介:Robert C. MartinObject Mentor公司總裁,面向物件設計、模式、UML敏捷方法學和極限程式設計領域內的資深顧問。他不僅是Jolt獲獎圖書《敏捷軟體開發:原則、模式與實踐》(中文版)(《敏捷軟體開發》(英文影印版))的作者,還是暢銷書Designing Object-Oriented C++ Applications Using the Booch Method的作者。MartinPattern Languages of Program Design 3More C++ Gems的主編,並與James Newkirk合著了XP in Practice。他是國際程式設計師大會上著名的發言人,並在C++ Report雜誌擔任過4年的編輯。

相關推薦

轉載效能調--永遠超乎想象 原文最終修訂 2006-08-28 晚上114838

未經博主同意轉載此部落格,如此好的部落格,後生只是看著心喜,便未經同意轉載,希望各位去原部落格閱讀:http://blog.csdn.net/rmartin/article/details/1132312 多年以前,我在開發一個C++的應用程式。我的同伴Jim Newk

效能調--永遠超乎想象 原文最終修訂 2006-08-28 晚上114838

多年以前,我在開發一個C++的應用程式。我的同伴Jim Newkirk(當時的)過來告訴說,我們的一個公用函式執行得非常的緩慢。這個函式是用來轉換二進位制的樹結構資料為普通文字,並存儲到檔案中的。(這是在XML出現之前,但概念類似於XML) 我審視了這個函式一會兒,發現了一

如何讓Ruby程式碼更簡練?!原文最終修訂 2006-08-18 下午024225

你可以用它來做什麼呢?請閱讀... 我四前年曾接觸過Ruby,就是為了看看這個語言到底什麼樣。我用了它一段時間然後就把注意力放到Fit,Fitness(譯註1),和Java/.Net上了。然而最近,隨著Rails的興起,我又開始關注Ruby了;也開始認識到這是一個多麼高效、親

Tomcat效能調以及遠端管理Tomcat manager與psi-probe監控

tomcat優化的我用到的幾個點: 1.記憶體優化 2.執行緒優化 docs/config/http.html maxConnections acceptCount(配置的太大是沒有意義的) maxThreads minSpareThreads 最小空閒的工作

轉載】JVM調工具的使用jps,jstat,jstack,jmap,jhat

文章目錄 一,jps命令 二 jstat命令 1 類載入統計: 2 編譯統計 3 垃圾回收統計 三 jstack命令 四 jmap 原文連結:http://bl

TomcatJVM效能調

Tomcat架構圖      Tomcat與JVM版本優化 Tomcat的執行是基於Java的虛擬機器。SUN的JVM動態庫有client和server兩個版本,分別針對桌面應用和伺服器應用做了相應的優化,client版本載入速度較快,server版本載入速度較慢但

Spark十二--效能調

一段程式只能完成功能是沒有用的,只能能夠穩定、高效率地執行才是生成環境所需要的。 本篇記錄了Spark各個角度的調優技巧,以備不時之需。 一、配置引數的方式和觀察效能的方式 額。。。從最基本的開始講,可能一些剛接觸Spark的人不是很清楚Spark的一

SQL Server 效能調2 之索引Index的建立

前言 索引是關係資料庫中最重要的物件之一,他能顯著減少磁碟I/O及邏輯讀取的消耗,並以此來提升 SELECT 語句的查詢效能。但它是一把雙刃劍,使用不當反而會影響效能:他需要額外的空間來存放這些索引資訊,並且當資料更新時需要一些額外開銷來保持索引的同步。 形象的來說索引就像

golang 效能調分析工具 pprof

[golang 效能調優分析工具 pprof(上)篇](https://www.cnblogs.com/jiujuan/p/14588185.html), 這是下篇。 ## 四、net/http/pprof ### 4.1 程式碼例子 1 > go version go1.13.9 把上面的程式例子稍

java效能調轉載

1.用new關鍵詞建立類的例項時,建構函式鏈中的所有建構函式都會被自動呼叫。但如果一個物件實現了Cloneable介面,我們可以呼叫它的clone()方法。clone()方法不會呼叫任何類建構函式。  在使用設計模式(Design Pattern)的場合,如果用Fa

ETL調的一些分享轉載

ant 想法 one gin targe 收集 commit 是否 可見 如在上篇文章《ETL調優的一些分享(上)》中已介紹的,ETL是構建數據倉庫的必經一環,它的執行性能對於數據倉庫構建性能有重要意義,因此對它進行有效的調優將十分重要。ETL業務的調優可以從若幹思路開展,

ETL調的一些分享轉載

經歷 信息 同時 讀取 ges 解決辦法 排除 硬件配置 times ETL是構建數據倉庫的重要一環。通過該過程用戶將所需數據提取出來,並按照已定義的模型導入數據倉庫。由於ETL是建立數據倉庫的必經過程,它的效率將影響整個數據倉庫的構建,因此它的有效調優具有很高的重要性。在

Spark官方調文檔翻譯轉載

區域 ng- 完整 好的 java類型 int 單個 rdd 常見 Spark調優 由於大部分Spark計算都是在內存中完成的,所以Spark程序的瓶頸可能由集群中任意一種資源導致,如:CPU、網絡帶寬、或者內存等。最常見的情況是,數據能裝進內存,而瓶頸是網絡帶寬;當

Spark之效能調總結

總結一下spark的調優方案: 一、效能調優   1、效能上的調優主要注重一下幾點:     Excutor的數量     每個Excutor所分配的CPU的數量     每個Excutor所能分配的記憶體量     Driver端分配的記憶體數量   2、如何分配資源     在生產環境中,

大資料之效能調方面資料傾斜、shuffle、JVM等方面

一、對於資料傾斜的發生一般都是一個key對應的資料過大,而導致Task執行過慢,或者記憶體溢位(OOM),一般是發生在shuffle的時候,比如reduceByKey,groupByKey,sortByKey等,容易產生資料傾斜。 那麼針對資料傾斜我們如何解決呢?我們可以首先觀看log日誌,以為log日誌報

雲端計算生產環境架構效能調和遷移套路總結以 AWS 為例

最近完成了一個雲端計算平臺應用的架構調優。客戶是一個 Wordpress + MySQL 的站點,剛從本地資料中心遷移到了 AWS,由於團隊技能限制,無法充分發揮雲端計算的優勢。加之應用程式在夜間高流量時段崩潰,架構優化和遷移迫在眉睫。本文以這次架構遷移經驗為例,介紹雲端計算架構優化遷移的基本步驟和

阿里資深架構師構造Java架構學習樹效能調+常用框架原始碼+微服務

效能調優專題 效能優化如何理解 JVM調優 JAVA程式效能優化 Tomcat Mysql 歡迎加入Java高階架構學習交流群:805685193 免費獲取Dubbo、Redis、設計模式、Netty、zookeeper、S

[jvm]五tomcat效能調效能監控visualvm

1、JDK記憶體優化 根據伺服器物理內容情況配置相關引數優化tomcat效能。當應用程式需要的記憶體超出堆的最大值時虛擬機器就會提示記憶體溢位,並且導致應用服務崩潰。因此一般建議堆的最大值設定為可用記憶體的最大值的80%。 Tomcat預設可以使用的記憶體為128MB,在較大型的應用專案中,

mysql效能調——聚簇索引、索引覆蓋

1、聚簇索引      這裡說的,聚簇索引是相對InnoDB資料庫引擎來說的,講的是聚簇索引隨機主鍵值的效率      對於InnoDB來說,主鍵儘量用整型,並且是遞增的比較好,因為新增的時候,如果是隨機主鍵插入,會存在節點分裂

mysql效能調——列選取原則

1、列型別選擇       1)整型 > data、time > char、varchar > blob            整型、date、time運算快 &nb