Clojure-替代在大型地图上使用缩减或改进性能的方法

Clojure-替代在大型地图上使用缩减或改进性能的方法,clojure,Clojure,我有一个中等大小的地图(~1M个键,具有相对较小的值,例如(first mymap)=>[“7:21658846-21658846”{[“C”“T”]{“中枢神经系统”1}]),使用下面的函数将其简化为另一个地图(省略了一些次要功能,因为它们与问题无关-它们不会影响性能) 新映射是使用reduce(reduce-kv)创建的,因为在解析原始映射中的每个元素时,我需要累计增加映射中的各种嵌套值 对于大约10万条参赛作品来说,这并不是一个真正的问题,大约需要40秒。对于我1米大小的地图来说,这是一个

我有一个中等大小的地图(~1M个键,具有相对较小的值,例如
(first mymap)=>[“7:21658846-21658846”{[“C”“T”]{“中枢神经系统”1}]
),使用下面的函数将其简化为另一个地图(省略了一些次要功能,因为它们与问题无关-它们不会影响性能)

新映射是使用reduce(reduce-kv)创建的,因为在解析原始映射中的每个元素时,我需要累计增加映射中的各种嵌套值

对于大约10万条参赛作品来说,这并不是一个真正的问题,大约需要40秒。对于我1米大小的地图来说,这是一个巨大的问题(在未来的跑步中,它只会变得更大),20分钟后还没有完成

是否有任何明显的问题很容易解决?非惯用的方法可以改变,以大幅提高性能?它似乎不是特别平行-但其中有些东西确实是低效的-我希望累积映射

谢谢你的建议

*编辑-在总和定义中添加了关联

(defn add-mut-freq-firstTS
  "Builds a map of Transcript -> {aapos -> {:aaposn_count :codon :aa {genomic_SNP_posn -> {:SNP_posn_count :frame {:genomic_ref :genomic_mut :SNP_count :aa_mut :codon_mut}}}}.
  but only using one transcript per SNP. ***ONLY USED FOR STATS CALCS***"
  [CDS-ref snp-freq]
  (reduce-kv (fn [m k v](let [aa-ref (first (cosu/map-ts-aa-pos CDS-ref [k v]))] (add-mut-freq** m aa-ref aa-ref [k v]))) {} snp-freq) )

(defn add-mut-freq**
  "Adds data for frequency of different mutations at a given position to a     cumulative map m for a given transcript. Updates running totals
  for frequency at aa position and genomic position as well."
  [m ts first-ts snp-freq]
  (let [[ts_ID SNP_aa_posn SNP_aa_frame _ gene strand] ts
    [posn nt-mut-freq] snp-freq
    m-pre (if (= ts first-ts) (assoc-in m [:ts ts_ID :snp-aa-pos SNP_aa_posn :snp-nt-posn posn :first] true) m)
    m-init (assoc-in m-pre [:ts ts_ID :gene] gene)]
(reduce-kv (fn [m1 k v](
          let [mut k
               tiss-freq v
               snp-count (apply + (vals tiss-freq))]
                     (-> m1 (u/assoc-in-sum [:ts ts_ID :ts-cnt] snp-count)
                         (assoc-in [:ts ts_ID :ts-strand] strand)
                         (u/assoc-in-sum [:ts ts_ID :snp-aa-pos SNP_aa_posn :aa-cnt] snp-count)
                         (u/assoc-in-sum [:ts ts_ID :snp-aa-pos SNP_aa_posn :snp-nt-posn posn :pos-cnt] snp-count)
                         (assoc-in [:ts ts_ID :snp-aa-pos SNP_aa_posn :snp-nt-posn posn :ts-frame] SNP_aa_frame)
                         (u/assoc-in-sum [:ts ts_ID :snp-aa-pos SNP_aa_posn :snp-nt-posn posn :mut-nt mut :posnt-cnt] snp-count)
                         (add-tissue-counts ts_ID SNP_aa_posn tiss-freq)
                         (assoc-in [:ts ts_ID :snp-aa-pos SNP_aa_posn :snp-nt-posn posn :mut-nt mut :mut-tiss-freq] tiss-freq))
          )) m-init nt-mut-freq)))

(defn add-tissue-counts
  [m ts_ID SNP_aa_posn tiss-map]
  (reduce-kv (fn [m1 k v] (-> m1 (u/assoc-in-sum [:ts ts_ID :snp-aa-pos SNP_aa_posn :aa-tiss-cnt k] v)
           (u/assoc-in-sum [:ts ts_ID :ts-tiss-cnt k] v)
           (u/assoc-in-sum [:tiss-cnt k] v)
           )) m tiss-map))

(defn assoc-in-sum
  "Same as assoc-in except that if the key already exists, the value is added to instead of replaced"
  [m key-vec v]
  (let [ex-val (get-in m key-vec)
        new-val (if ex-val (+ ex-val v) v)]
  (assoc-in m key-vec new-val))
  )

在持久数据的基础上,您正在进行大量的缩减,使用有状态传感器可以提高速度

这里有一个关于如何使用volatives创建transformer函数的小例子,也许它可以为创建更快版本的代码提供一些想法

(defn测试xf
[射频]
(设[sum(volatile!0)]
(fn
([](rf))
([结果](rf(关联结果:总和@总和)))
([结果[km]]
计算总数等。
(vswap!sum+(输入m[“C”“T”]“x”]))
结果是还原时的瞬态映射!
(->结果
(assoc!:mydata“你好”)
(助理秘书长)
))))
(定义数据[n]
(适用于[i(范围n)]
[(str“key-”i{[“C”T]{“x”1}}]))
(时间
(:总数)
(进入{}测试xf(数据1000000)))
“运行时间:1750.867127毫秒”
=> 1000000

一个低挂果实是,您正在共享公共前缀的路径上执行许多assoc in(-sum)。您应该将它们分组到公共前缀上的更新in中,并展开剩余的
*-in
调用。另外
assoc in sum
(udpate in m ks(fnil+0)v)
。浏览一下,也许是@birdspider的时候了,你会得到零警告:这里根本没有java互操作。对于阅读本文的其他人来说,一些关于这个主题的好文章帮助了我;可能也是一个有趣的库。它的许多功能都可以使用核心库完成,但它有一个很好的自述文本解释了数据集折叠和并行处理背后的关键概念。好的-最后,有状态传感器很酷,我从经验中学到了一些东西,但在这种情况下没有真正的帮助。原因是有很多运行求和需要保留(图的目的)实现传感器的好处微乎其微。请投票给我的信息和建议,因为我从追兔子到那个特定的洞里学到了很多,但在这种情况下,我不能把它作为一个答案。谢谢mangolas@statler好的,很高兴听到这个消息,至少这是一次学习经历。除了(有时)更快的传感器组合得非常好,它可能会将计算分离到不同的函数,使事情更容易理解并找到瓶颈。祝你好运。