我刚刚完成一个程序,它基本上收集数据并计算几个表(通常由过滤器或聚合级别(我写了一个SELECT + GROUP BY,如Clojure函数),我最终用来计算汇总表。这个汇总表以后使用,只有30 000行。
为了让你明白,这是我必须启动的命令:
(def summary-pages (merge-summary-pages (merge-pages-stock (compute-stock) (compute-pages) (compute-xx-prices) (compute-g) (compute-brands) (compute-prices) (compute-sales) (compute-sales-month))
(merge-full-pages-per-page (merge-full-pages (merge-pages-stock (compute-stock) (compute-pages) (compute-xx-prices) (compute-g) (compute-brands) (compute-prices) (compute-sales) (compute-sales-month)) (merge-pages-stock-excluded (compute-pages) (compute-stock) (compute-g) (compute-brands) (compute-prices) (compute-sales) (compute-sales-month))))
(merge-pages-stock-per-page (merge-pages-stock (compute-stock) (compute-pages) (compute-xx-prices) (compute-g) (compute-brands) (compute-prices) (compute-sales) (compute-sales-month)))
(merge-affectations-count (compute-affectations))))
如您所见,我会多次调用相同的数据(实际上(compute-pages)
调用compute-affectations
。
这样可行,但问题是compute-pages
,特别是compute-affectations
在Google BQ(1500万行)和microsoft sql server(45百万行)上都是非常庞大的查询。
问题是,查询它们需要花费时间4-5次,而且我也害怕打破数据库
另一个问题是我必须计算所有compute-affectations
因为它来自sql server而我的左连接使用group-by。
我尝试用def
拆分作业但是我有GC开销错误。
因为我可以在计算后清理affectations
,我试过
(def affectations nil)
...不会改变任何内容,我在microsoft命令面板中看不到内存空闲。
有没有办法清理记忆?
在Python中我的程序完全没有问题(实际上内存使用率最多为80%)但是这里的堆空间为13gb,当我对任何子数据使用def时,我总是遇到问题。 我有16GB的ram所以我不能增加堆空间,而且我觉得奇怪的是这样的“小”数据占用了这么多内存。
我用csv计算测试数据,基础数据只有3.3gb ......
编辑
工作代码(部分内容):
(let [_ (init-config! path)
_ (write-affectations)
_ (write-pages)
_ (spit "prepared-pages.edn" (prn-str (prepare-pages (read-pages))) :append :true)
_ (write-stock)
_ (write-prices)
_ (write-xx-prices)
_ (write-brands)
_ (write-g)
_ (write-vehicles)
_ (write-sales)
_ (write-sales-month)
_ (System/gc)
stock (read-stock)
affectations (read-affectations)
pages (read-pages)
prepared-pages (prepare-pages pages)
xx-prices (read-xx-prices)
g (read-g)
brands (read-brands)
prices (read-prices)
sales (read-sales)
sales-month (read-sales-month)
pages-stock (merge-pages-stock stock prepared-pages xx-prices g brands prices sales sales-month)
pages-stock-excluded (merge-pages-stock-excluded prepared-pages stock g brands prices sales sales-month)
full-pages-per-page (-> (merge-full-pages pages-stock pages-stock-excluded)
(merge-full-pages-per-page))
pages-stock-per-page (merge-pages-stock-per-page pages-stock)
affectations-count (merge-affectations-count affectations)
summary-pages (doall (merge-summary-pages pages-stock full-pages-per-page pages-stock-per-page affectations-count))
_ (System/gc)
_ (io/delete-file "affectations.edn")
_ (io/delete-file "pages.edn")
_ (io/delete-file "prepared-pages.edn")
_ (io/delete-file "stock.edn")
_ (io/delete-file "prices.edn")
_ (io/delete-file "xx-prices.edn")
_ (io/delete-file "brands.edn")
_ (io/delete-file "g.edn")
_ (io/delete-file "vehicles.edn")
_ (io/delete-file "sales.edn")
_ (io/delete-file "sales-month.edn")
我在HDD(.edn文件)上写了查询内容,然后我懒洋洋地读它并将其传递给函数。
谢谢!
答案 0 :(得分:2)
如果不确切知道这些是什么,很难检查以下情况:
最后一个显然是你问题的一部分(或者也许是全部)。我重复了你的例子,所以每个值只计算一次。
(def summary-pages
(let [stock (compute-stock)
pages (compute-pages)
prices (compute-prices)
brands (compute-brands)
sales-month (compute-sales-month)
g (compute-g)
sales (compute-sales)
xx-prices (compute-xx-prices)
pages-stock (merge-pages-stock stock pages xx-prices g brands prices sales sales-month)
pages-stock-excluded (merge-pages-stock-excluded pages stock g brands prices sales sales-month)
full-pages-per-page (merge-full-pages-per-page (merge-full-pages pages-stock pages-stock-excluded))
pages-stock-per-page (merge-pages-stock-per-page pages-stock)
affectations-count (merge-affectations-count (compute-affectations))]
(merge-summary-pages pages-stock
full-pages-per-page
pages-stock-per-page
affectations-count)))
您的下一步是注释掉除第一个之外的所有内容,确认它在合理的时间内正确运行。然后取消注释下一个并重复。
<小时/> 由于您的数据很大,并且您希望您的代码一次使用延迟序列来处理它的窗口,我想向您介绍如何&#34;本地绑定&#34;和#34;当地人清理&#34;在Clojure工作。
这是一个&#34;应该&#34;的序列的例子。通过按住十亿个整数序列的头来导致内存不足:
user> (time
(let [huge-sequence (range 1e9)]
(last huge-sequence)))
"Elapsed time: 49406.048091 msecs"
999999999
但它没有?为什么?
它必须以某种方式弄清楚它实际上并不需要存储巨大序列的头部,因为之后没有人会使用它!因此,让我们通过更改示例来测试它必须保持头部并查看它是否仍然有效
user> (time
(let [huge-sequence (range 1e9)]
(last huge-sequence)
(first huge-sequence)))
这次我的CPU风扇启动,所有8个处理器立即旋转到100%。五分钟后,我开始变得不耐烦了,所以我会去吃午饭,看看我吃饭的时候是否吃完了。到目前为止,我们的假设看起来很准确。
因此,当您在一个大的let
块中定义一堆序列然后进行一次使用它们的函数调用时,并且在该调用之后从不接触它们,序列的头部通常可以是在功能调用时清除,它将会#34;神奇地&#34;工作。这就是我建议你从上到下一次测试这些的原因。
几乎在Clojure中从来没有理由两次定义相同的数据,我们在这里认真考虑参考透明度。
<小时/> 更新:测试最终耗尽内存,虽然时间调用被抛出的异常所绕过,所以我不知道实际花了多长时间:
user> (time
(let [huge-sequence (range 1e9)]
(last huge-sequence)
(first huge-sequence)))
OutOfMemoryError Java heap space [trace missing]