Performance Clojure跑得非常慢

Performance Clojure跑得非常慢,performance,optimization,clojure,numbers,biginteger,Performance,Optimization,Clojure,Numbers,Biginteger,我制作了一个简单的Clojure程序,即使迭代次数等于500次,它的运行速度也非常慢。谁能告诉我我的代码有什么问题吗 (ns calc.core (:require [clojure.math.numeric-tower :as math])) (def ^:const B 500) (defn build-map [p h g] (into {} (for [n (range 0 B)] [n (mod (/ h (math/expt g n)) p)]))) (defn find-

我制作了一个简单的Clojure程序,即使迭代次数等于500次,它的运行速度也非常慢。谁能告诉我我的代码有什么问题吗

(ns calc.core
(:require [clojure.math.numeric-tower :as math]))

(def ^:const B 500)

(defn build-map [p h g]
  (into {} (for [n (range 0 B)] [n (mod (/ h (math/expt g n)) p)])))

(defn find-result [res-map p g]
  (for [n (range 0 B)
    :when (= (mod (math/expt g (* B n)) p) (res-map n))]
  (get res-map n)))

(defn calculate [p h g]
  (find-result (build-map p h g) p g))

(def ^:const P 130N)
(def ^:const G 642N)
(def ^:const H 323N)    

(defn -main []
  (do
    (println "Starting calculation...")
    (println (calculate P H G))
    (println "done")))
更新#1 我对find result函数做了一些更改,性能得到了改进:

(defn find-result [res-map p g]
  (for [[k v] res-map
    :when (= (mod (math/expt g (* B k)) p) v)]
  v))
为什么这段代码运行得更快?我的代码还可以做哪些改进,以使其运行更快(对于B=1024,运行时间仍然会变慢)

更新#2

尝试了所有的建议,但Clojure版本似乎永远运行。例如,这个用Java编写的版本运行速度非常快:

下面是我的代码的更新版本:

(ns crypto5.core
  (:require [clojure.math.numeric-tower :as math]))

(def ^:const B (math/expt 2N 10N))
(def ^:const P 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171N)
(def ^:const G 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568N)
(def ^:const H 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333N)
(def ^:const R (/ 1 G))
(def GpowB (math/expt G B))
(def exps (take B (iterate (fn [[e eb]] [(* e G) (* eb GpowB)]) [1 1])))

(defn -main []
  (println (map #(mod (/ H (first %)) P) (filter (fn [[e eb]] (= (mod (/ H e) P) (mod eb P))) exps))))

你在计算大量的数字。当B为500时,地图上的键为0-499。在g为642N的find result中,计算最大值(math/expt 642N 249500)的math/expt是500倍。仅此一项计算就需要很长时间,而这正是k=499的时候。如果您指定了您正试图解决的确切问题,这将很有帮助。看起来更像是一个算法问题

更新

(ns calc.core)

(def start (biginteger 1))
(def b (.pow (biginteger 2) 20))
(def g (biginteger 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568))
(def p (biginteger 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171))
(def h (biginteger 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333))

(defn build-left-table [start end p h g]
  (into {}
        (pmap (fn [n] [(.mod(.multiply h (.modInverse (.modPow g (biginteger n) p) p)) p) (biginteger n)])
              (range start (inc end)))))

(defn find-collision [table b p g]
  (let [base (.modPow g b p)]
    (loop [i 0
           v (biginteger 1)]
      (when (< i b)
        (do
          (if (contains? table v)
               (let [percentage (double (/ (* i 100) b))]
                 (println (str "Collision after " percentage "%"))
                 [i (table v)])
               (recur (inc i) (.mod (.multiply v base) p))))))))

(time (find-collision (build-left-table start b p h g) b p g))
我在此处创建了Clojure解决方案:。据我所知,它应该与Java版本相同(最好检查一下以确定)。它也可以在30秒左右运行,而且体积更小,可读性也更高。具有讽刺意味的是,我过去很难阅读Clojure,现在读Java时我遇到了麻烦:)。您遇到的最大性能问题是不使用modPow。事实证明,即使在Java中,使用pow和mod也比使用modPow慢得多。哦,Clojure BigInt和BigInteger不一样,这并不能让它更简单。所以我求助于使用BigInteger和Java互操作。有时候,你需要获得最好的表现。希望这有帮助

更新代码#2

(ns calc.core)

(def start (biginteger 1))
(def b (.pow (biginteger 2) 20))
(def g (biginteger 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568))
(def p (biginteger 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171))
(def h (biginteger 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333))

(defn build-left-table [start end p h g]
  (into {}
        (pmap (fn [n] [(.mod(.multiply h (.modInverse (.modPow g (biginteger n) p) p)) p) (biginteger n)])
              (range start (inc end)))))

(defn find-collision [table b p g]
  (let [base (.modPow g b p)]
    (loop [i 0
           v (biginteger 1)]
      (when (< i b)
        (do
          (if (contains? table v)
               (let [percentage (double (/ (* i 100) b))]
                 (println (str "Collision after " percentage "%"))
                 [i (table v)])
               (recur (inc i) (.mod (.multiply v base) p))))))))

(time (find-collision (build-left-table start b p h g) b p g))

你在计算大量的数字。当B为500时,地图上的键为0-499。在g为642N的find result中,计算最大值(math/expt 642N 249500)的math/expt是500倍。仅此一项计算就需要很长时间,而这正是k=499的时候。如果您指定了您正试图解决的确切问题,这将很有帮助。看起来更像是一个算法问题

更新

(ns calc.core)

(def start (biginteger 1))
(def b (.pow (biginteger 2) 20))
(def g (biginteger 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568))
(def p (biginteger 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171))
(def h (biginteger 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333))

(defn build-left-table [start end p h g]
  (into {}
        (pmap (fn [n] [(.mod(.multiply h (.modInverse (.modPow g (biginteger n) p) p)) p) (biginteger n)])
              (range start (inc end)))))

(defn find-collision [table b p g]
  (let [base (.modPow g b p)]
    (loop [i 0
           v (biginteger 1)]
      (when (< i b)
        (do
          (if (contains? table v)
               (let [percentage (double (/ (* i 100) b))]
                 (println (str "Collision after " percentage "%"))
                 [i (table v)])
               (recur (inc i) (.mod (.multiply v base) p))))))))

(time (find-collision (build-left-table start b p h g) b p g))
我在此处创建了Clojure解决方案:。据我所知,它应该与Java版本相同(最好检查一下以确定)。它也可以在30秒左右运行,而且体积更小,可读性也更高。具有讽刺意味的是,我过去很难阅读Clojure,现在读Java时我遇到了麻烦:)。您遇到的最大性能问题是不使用modPow。事实证明,即使在Java中,使用pow和mod也比使用modPow慢得多。哦,Clojure BigInt和BigInteger不一样,这并不能让它更简单。所以我求助于使用BigInteger和Java互操作。有时候,你需要获得最好的表现。希望这有帮助

更新代码#2

(ns calc.core)

(def start (biginteger 1))
(def b (.pow (biginteger 2) 20))
(def g (biginteger 11717829880366207009516117596335367088558084999998952205599979459063929499736583746670572176471460312928594829675428279466566527115212748467589894601965568))
(def p (biginteger 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084171))
(def h (biginteger 3239475104050450443565264378728065788649097520952449527834792452971981976143292558073856937958553180532878928001494706097394108577585732452307673444020333))

(defn build-left-table [start end p h g]
  (into {}
        (pmap (fn [n] [(.mod(.multiply h (.modInverse (.modPow g (biginteger n) p) p)) p) (biginteger n)])
              (range start (inc end)))))

(defn find-collision [table b p g]
  (let [base (.modPow g b p)]
    (loop [i 0
           v (biginteger 1)]
      (when (< i b)
        (do
          (if (contains? table v)
               (let [percentage (double (/ (* i 100) b))]
                 (println (str "Collision after " percentage "%"))
                 [i (table v)])
               (recur (inc i) (.mod (.multiply v base) p))))))))

(time (find-collision (build-left-table start b p h g) b p g))
您的算法是O(n logn),因为expt对每个n进行logn乘法。您可以将其简化为O(n),以更实用的方式再次写入。我的食谱:

  • 预计算g^B:
    (def GpowB(数学/表达式g B))

  • 迭代计算指数:
    (取B(迭代(fn[[eb][(*e G)(*eb GpowB)])[1]))

  • 过滤此序列以获取解决方案并获取结果:
    (map#(mod(/H(first%))p)(filter(fn[[e eb]](=(mod(/H e)p)(mod eb p)))exps

  • 把它当作一个暗示,我现在还不能测试它

    在更新过程中,由于避免了在映射中对每个元素调用
    (res-map n)
    ,所以更新速度更快。

    您的算法是O(n logn),因为expt对每个n进行logn乘法。您可以将其简化为O(n),以更实用的方式再次写入。我的食谱:

  • 预计算g^B:
    (def GpowB(数学/表达式g B))

  • 迭代计算指数:
    (取B(迭代(fn[[eb][(*e G)(*eb GpowB)])[1]))

  • 过滤此序列以获取解决方案并获取结果:
    (map#(mod(/H(first%))p)(filter(fn[[e eb]](=(mod(/H e)p)(mod eb p)))exps

  • 把它当作一个暗示,我现在还不能测试它


    在您的更新中,您会更快,因为您避免了在映射中对每个元素调用
    (res map n)

    给定这些常量,
    (mod(/h(math/expt g n))p)
    对于大于0的每n将小于1。这真的是你想要的吗?给定这些常数,
    (mod(/h(math/expt g n))p)
    对于大于0的每n将小于1。这真的是你想要的吗?是的,数字很大。实际上,目标数字pg和H将是130个数字长。Python中的类似代码可以用130位数字在大约2分钟内完成所有计算,因此我认为算法没有问题。您能用目标数字显示Python代码吗?这将使比较更容易。我已经用Java代码链接了一个要点,在30秒内解决了这个问题。更新了答案。用Clojure解决方案创建了一个要点。希望这能解决您的问题。顺便说一句,不需要find collision中的“do”块。忘了把它拿走。是的,数字很大。实际上,目标数字pg和H将是130个数字长。Python中的类似代码可以用130位数字在大约2分钟内完成所有计算,因此我认为算法没有问题。您能用目标数字显示Python代码吗?这将使比较更容易。我已经用Java代码链接了一个要点,在30秒内解决了这个问题。更新了答案。用Clojure解决方案创建了一个要点。希望这能解决您的问题。顺便说一句,不需要find collision中的“do”块。忘了取下我的