原子在大地图上使用时速度很慢
atom is slow when using it with big map
我有一个使用 clojure 的 ETL,每个线程可以加载文件的不同部分,它还需要从业务密钥中获取密钥。存储业务键到键映射的数据结构是一个哈希映射,如:
{"businessKey1" 1,
"businessKey2" 2,
"businessKey3" 3,
"businessKey4" 4,
"businessKey5" 5 }
当 ETL 从文件中加载数据时,它会将文件中的每一行解析为列,如果可以在映射中找到业务键列,则只需 return 键,例如,如果找到 businessKey1,则return 1. 但是如果找到了businessKey6,那么需要调用一个web service来创建一个新的key。我打算使用atom,所以当每个线程找到一个新的key时,使用atom来修改map。但是表现非常糟糕。我测试了下面的代码,很慢,而且GC很多activity.
(def a (atom {}))
(map #(swap! a (partial merge {% 1})) (range 10000))
(println a)
最好的解决方案是什么?我应该在 java 中使用 ConcurrentHashMap 吗?
性能不佳的主要原因似乎是 (partial merge {% 1})
的使用
更惯用的形式如下:
(let [a (atom {})]
(doall (map #(swap! a merge {% 1}) (range 10000))) (println @a)))
更快的是使用 assoc
而不是每次都创建临时地图:
(let [a (atom {})]
(doall (map #(swap! a assoc % 1) (range 10000))) (println @a)))
如果你想迭代一个 seq 的副作用,最好使用 doseq
:
(count (let [a (atom {})] (doseq [r (range 10000)] (swap! a assoc r 1))))
一个原子不是必须的,你想要的可以表示为一个缩减:
(count (reduce (fn [m r] (assoc m r 1)) {} (range 10000)))
您可以通过使用 Clojure 避免在此处使用原子 reducers
:
(require '[clojure.core.reducers :as r])
(defn lookup [k]
; do remote call, here it just returns 1
1)
(defn f
([] {})
([acc k] (if (get acc k)
acc
(assoc acc k (lookup k)))))
(r/fold merge f (vec (range 10000)))
clojure.core.reducers/fold
将自动 运行 并行并合并结果。
我有一个使用 clojure 的 ETL,每个线程可以加载文件的不同部分,它还需要从业务密钥中获取密钥。存储业务键到键映射的数据结构是一个哈希映射,如:
{"businessKey1" 1,
"businessKey2" 2,
"businessKey3" 3,
"businessKey4" 4,
"businessKey5" 5 }
当 ETL 从文件中加载数据时,它会将文件中的每一行解析为列,如果可以在映射中找到业务键列,则只需 return 键,例如,如果找到 businessKey1,则return 1. 但是如果找到了businessKey6,那么需要调用一个web service来创建一个新的key。我打算使用atom,所以当每个线程找到一个新的key时,使用atom来修改map。但是表现非常糟糕。我测试了下面的代码,很慢,而且GC很多activity.
(def a (atom {}))
(map #(swap! a (partial merge {% 1})) (range 10000))
(println a)
最好的解决方案是什么?我应该在 java 中使用 ConcurrentHashMap 吗?
性能不佳的主要原因似乎是 (partial merge {% 1})
更惯用的形式如下:
(let [a (atom {})]
(doall (map #(swap! a merge {% 1}) (range 10000))) (println @a)))
更快的是使用 assoc
而不是每次都创建临时地图:
(let [a (atom {})]
(doall (map #(swap! a assoc % 1) (range 10000))) (println @a)))
如果你想迭代一个 seq 的副作用,最好使用 doseq
:
(count (let [a (atom {})] (doseq [r (range 10000)] (swap! a assoc r 1))))
一个原子不是必须的,你想要的可以表示为一个缩减:
(count (reduce (fn [m r] (assoc m r 1)) {} (range 10000)))
您可以通过使用 Clojure 避免在此处使用原子 reducers
:
(require '[clojure.core.reducers :as r])
(defn lookup [k]
; do remote call, here it just returns 1
1)
(defn f
([] {})
([acc k] (if (get acc k)
acc
(assoc acc k (lookup k)))))
(r/fold merge f (vec (range 10000)))
clojure.core.reducers/fold
将自动 运行 并行并合并结果。