心不停歇,希望就始終存在

          專(zhuān)注&堅(jiān)持
          posts - 11, comments - 9, trackbacks - 0, articles - 0
            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

          Clojure 性能 tips(翻譯)

          Posted on 2012-07-29 14:15 steven.cui 閱讀(1960) 評(píng)論(2)  編輯  收藏 所屬分類(lèi): clojure

          原文章寫(xiě)在Google Groups thread里,但是還是值得再說(shuō)下。

          有朋友把Java和Clojure的一些代碼片段放在Clojure Google group里比較,并提到Java的性能要比Clojure快太多了,疑問(wèn)到底Clojure能不能趕上Java?

          在我的一個(gè)開(kāi)源項(xiàng)目clj-starcraft中,關(guān)于java的性能問(wèn)題,實(shí)際上也是我始終面對(duì)的,在我寫(xiě)這篇文章的時(shí),我的Clojure代碼還是慢了Java代碼6倍(Clojure花了70秒解析了1050個(gè)文件,Java則只有12秒)

          然而,70秒對(duì)過(guò)去的速度而言不算太糟糕,在剛開(kāi)始的時(shí)候,竟然花了10分鐘來(lái)分析1050個(gè)文件。甚至比我用Python實(shí)現(xiàn)的還要慢。

          感謝Java的profiler和熱情的Clojure朋友,下面列出了我在提升Clojure性能方面的一些tips:


          (set! *warn-on-reflection* true)

          這恐怕是最重要的一個(gè)提升:打開(kāi)這個(gè)設(shè)置將會(huì)警告你在任何一處用到Java反射API的方法和屬性。如你所想,直接調(diào)用永遠(yuǎn)比反射要快,不管哪里Clojure都會(huì)你不能解析這個(gè)方法,你需要自己用type hint方式來(lái)避免反射調(diào)用。關(guān)于使用type hint,Clojure官方站點(diǎn)給了一個(gè)如何使用和提速的例子。

          修復(fù)所有關(guān)于*warn-on-reflection* 的編譯警告后,我的clj-starcraft從10分鐘降到了3分半。


          強(qiáng)制設(shè)置數(shù)據(jù)類(lèi)型

          Clojure可以使用Java的基礎(chǔ)數(shù)據(jù)類(lèi)型,無(wú)論何時(shí)在循環(huán)的時(shí)候,堅(jiān)決考慮將你的值強(qiáng)制轉(zhuǎn)換成基礎(chǔ)類(lèi)型,這將大幅提高你的性能。基礎(chǔ)數(shù)據(jù)類(lèi)型在Clojure官方網(wǎng)站有例子和如何進(jìn)行強(qiáng)制轉(zhuǎn)換來(lái)提高性能。


          使用二元運(yùn)算符

          Clojure可以在一行里面支持多個(gè)表達(dá)式,但對(duì)于運(yùn)算操作符,只有在兩個(gè)的時(shí)候才被inlined,如果你發(fā)現(xiàn)自己的運(yùn)算符已經(jīng)超過(guò)了兩個(gè),或許該考慮重寫(xiě)你的代碼讓操作符顯示的成為兩個(gè)。下面請(qǐng)看兩者之間的比較:

          user> (time (dotimes [_ 1e7] (+ 2 4 5)))

          "Elapsed time: 1200.703487 msecs"

          user> (time (dotimes [_ 1e7] (+ 2 (+ 4 5))))

          "Elapsed time: 241.716554 msecs"


          使用==代替=

          使用==比較數(shù)字來(lái)代替=,提升性能那是相當(dāng)明顯:

          user> (time (dotimes [i 1e7] (= i i)))

          "Elapsed time: 230.797482 msecs"

          user> (time (dotimes [i 1e7] (== i i)))

          "Elapsed time: 5.143681 msecs"


          避免vectors的destructing binding

          在一段循環(huán)種,如果你想為了提升可讀性從vector中傳出值,考慮下標(biāo)訪問(wèn)來(lái)代替destructing binding。雖然代碼看起來(lái)更清晰,但卻非常慢。

          user> (let [v [1 2 3]]

                  (time

                   (dotimes [_ 1e7]

                     (let [[a b c] v]

                       a b c))))

          "Elapsed time: 537.239895 msecs"

          user> (let [v [1 2 3]]

                  (time

                   (dotimes [_ 1e7]

                     (let [a (v 0)

                           b (v 1)

                           c (v 2)]

                       a b c))))

          "Elapsed time: 12.072122 msecs"


          優(yōu)先使用本地變量

          如果你需要在循環(huán)中查詢(xún)一個(gè)值,你或許需要考慮使用本地變量(通過(guò)let定義)來(lái)代替全局變量??聪聝烧叩臅r(shí)間對(duì)比:

          user> (time

                 (do

                   (def x 1)

                   (dotimes [_ 1e8]

                     x)))

          "Elapsed time: 372.373304 msecs"

          user> (time

                 (let [x 1]

                   (dotimes [_ 1e8]

                     x)))

          "Elapsed time: 3.479041 msecs"

          如果你想使用本地變量來(lái)提升性能,可以考慮下面比較土的式的方式來(lái)避免全局變量:

          (let [local-x x]

            (defn my-fn [a b c]

              ...))

          使用profiler工具:

          JVM有兩個(gè)profiler工具, -Xprof和-Xrunhprof,找到程序瓶頸而不是瞎猜。


          最后說(shuō)明:

          你已經(jīng)注意到,在這些性能提升中,通過(guò)調(diào)用百萬(wàn)量的執(zhí)行來(lái)提升了幾百毫秒的性能。所以,不到萬(wàn)不得已需要提升性能的時(shí)候,沒(méi)必要讓你的代碼看起來(lái)不夠清晰。

          原文地址: http://gnuvince.wordpress.com/2009/05/11/clojure-performance-tips/

          最后補(bǔ)充:可以通過(guò)指定編譯為static方法來(lái)提高性能:

          pasting

           

          (defn
            ^{:static true}
            fib
            [n]
            (loop [a (long 1) b (long 1) i (long 1) r (list 1 1)]
              (if (== n i)
              r
              (recur b (+ a b) (inc i) (conj r (+ a b))))))

           

           


          評(píng)論

          # re: Clojure 性能 tips(翻譯)  回復(fù)  更多評(píng)論   

          2014-01-23 09:32 by Syeerzy
          在我的筆記本電腦上測(cè)試, 性能比上面的數(shù)字平均大約高 200 倍左右.
          而且(注意我改了數(shù)字,我的重復(fù)次數(shù)是上文的100倍)

          user=> (time (dotimes [_ 1e9] (+ 2 4 5)))
          "Elapsed time: 318.793 msecs"
          nil
          user=> (time (dotimes [_ 1e9] (+ 2 (+ 4 5))))
          "Elapsed time: 322.425 msecs"
          nil
          user=> (time (dotimes [i 1e9] (== i i)))
          "Elapsed time: 315.532 msecs"
          nil
          user=> (time (dotimes [i 1e9] (= i i)))
          "Elapsed time: 316.966 msecs"
          nil

          強(qiáng)制設(shè)置類(lèi)型,使用二元運(yùn)算符,使用==代替= 等操作幾乎沒(méi)有帶來(lái)可以觀察到的性能提升,而不是文中說(shuō)的幾十倍.

          另外,避免vectors的destructing binding實(shí)測(cè)提升性能70%左右(而不是文中的20幾倍)
          優(yōu)先使用本地變量實(shí)測(cè)提升性能20%-30%(而不是文中的12倍)


          基本上所有文中說(shuō)的提升性能的操作都是不劃算的. 此文有嚴(yán)重誤導(dǎo)傾向. 所有測(cè)試都照文中給的代碼, 并且測(cè)試20次以上.

          # re: Clojure 性能 tips(翻譯)  回復(fù)  更多評(píng)論   

          2014-02-13 22:50 by steven.cui
          @Syeerzy
          具體性能提高多少可能跟jvm版本以及clj版本有區(qū)別,這個(gè)應(yīng)該還是在clojure1.3之前的文章,現(xiàn)在的版本估計(jì)已經(jīng)足夠聰明了,還是那句話(huà)過(guò)度優(yōu)化和過(guò)早優(yōu)化都是惡魔。@Syeerzy

          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 玉溪市| 南川市| 兰考县| 闵行区| 西丰县| 石狮市| 灵山县| 那曲县| 无锡市| 仪征市| 连平县| 泉州市| 偃师市| 洛川县| 全州县| 罗山县| 当阳市| 凤台县| 浙江省| 游戏| 河东区| 嘉义市| 土默特左旗| 仙游县| 红桥区| 普宁市| 成武县| 会同县| 永清县| 甘泉县| 南漳县| 高台县| 台山市| 大宁县| 江北区| 紫云| 黔西县| 武山县| 玉树县| 柳河县| 霍山县|