2016-04-25 6 views
1

私は、基本的にデータを収集し、いくつかのテーブルを計算するプログラムを完成させました(私は最終的にサマリーテーブルを計算するために、一般的にフィルタや集計レベル(Clojure関数のようなSELECT + GROUP BYを書いた)です)。後で使用し、唯一の30 000行の長であるClojure:オブジェクトをメモリから削除するにはどうしたらいいですか?

あなたが理解させるために、これは私が起動する必要がありコマンドです:。

(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(15百万行)とMicrosoft SQL Server(45百万行)で非常に巨大なクエリです。

問題は、それらを4〜5回クエリするのに時間がかかることです。また、データベースを破壊することも恐れています。
もう1つの問題は、すべてcompute-affectationsを計算する必要があるということです。これはSQL Serverと私の左結合の使用によるものですからです。

私はdefでジョブを分割しようとしましたが、GCオーバーヘッドエラーがあります。私はいくつかの計算の後affectationsをきれいにすることができます ので、私はマイクロソフトコマンドパネルのメモリが自由に表示されていない、何も変更はありません...

(def affectations nil) 

を試してみました。

メモリをクリーンアップする方法はありますか?

私のプログラムはまったく問題なく動作します(実際にはメモリ使用量は最大で80%ですが)ここでは13GBのヒープ領域があります。 私は16GBのRAMを持っていますので、ヒープスペースを増やすことはできません。さらに、このような「小さい」データには非常に多くのメモリが必要なことは奇妙なことです。

Iは、CSVでテストデータを計算し、基本データは、コード(その一部)の操作のみ3.3ギガバイト...

EDIT

である:私は書き込み

  (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

'コンピュート・ページへのすべてのものを呼び出した場合:タイムコールがそのように私はそれが実際にかかった時間がわからないスローされた例外によってバイパスされたもののテストは最終的に、メモリ不足'または' compute-affectations'は毎回同じデータを返しています。なぜ 'let'を使わないのですか? –

+0

こんにちは、ありがとう私もそれを試しましたが、私の主な部分にあるので、実際に私のすべてのデータは大きなレットにあります。実際に私が説明したコマンドでさえ、SQL ServerとBQでは動作しません。私は愚かな微調整を見つけましたが、うまくいきました。まず、これらの2つの特定のテーブルをクエリし、HDD(CSV)にチャンクですぐに書き込み、それを(compute-summary-pages)に渡します。私は専門のプログラマーではないので、何が起こっているのか、実際の手掛かりはありません。文字列データが多すぎるメモリ(BQ)を消費したり、SQLクエリが遅延していませんか? –

+0

ところで、これは良いメモリプロファイラが解決に非常に役立つという問題のクラスです。あなたがOSSの仕事のために独占的にそれを使用していて、無料のライセンスを要求していない限り、お金がかかりますが、YourKitは優れた仕事をします。 –

答えて

2

:巨大な怠惰なシーケンスの頭部に保持

  • 怠惰なバグ(コードの怠惰な配列を読み取る前に、DB接続を閉じます結果)
  • メモリ内の同じもののコピーが多すぎます。

この最後の1つは、明らかに問題の一部(またはおそらくすべて)です。あなたの例を重複しないようにして、各値が1回だけ計算されるようにします。

(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))) 

あなたの次のステップは、それが正しくかつ妥当な時間内に実行されることを確認し、最初の1以外のすべてをコメントアウトすることです。次に、コメントを外して繰り返します。


あなたのデータが大きいので、レイジーシーケンスを使用して一度にそのウィンドウを操作したい場合は、「ローカルバインディング」と「ローカルクリア」の方法をご紹介します。 Clojureで働く。ここ

は億整数のシーケンスの頭部を保持することにより、メモリ例外のうちを引き起こす「すべきである」というシーケンスの例である:

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%にスピンアップ。 5分後、私はせっかちな気分になり始めたので、私は昼食に出かけて、私が食べている間に終わるかどうかを見ます。これまでのところ、私たちの仮説は正確に見ています。

大きなブロックのブロックを定義し、その呼び出しの後で決してそれらに触れることのない単一の関数呼び出しを行い、その呼び出しの後に決してそれらに触れるとき、シーケンスの先頭は通常関数それは「魔法のように」働くだけです。これが私があなたが上から下へこれらのものを一度にテストすることを提案した理由です。

がほぼです。同じデータを2回定義する理由は決してありません。私たちはここで真剣に参照の透明性を取ります。


更新:

user> (time 
     (let [huge-sequence (range 1e9)] 
     (last huge-sequence) 
     (first huge-sequence))) 
OutOfMemoryError Java heap space [trace missing] 
+0

私も試してみますが、うまくいく方法を理解すれば、怠惰な読者をletで束縛すると、すべてのシーケンスを一度計算すれば、それは記憶に残ります。この大きな計算(私のコード)を実行すると、一度スーパーデータが計算されると(ページあたりのフルページと言う)、中間データが消去されるのでメモリが大きく低下します。そのため、それはすべてを結びつけることとは対照的です。私はすべてのアプリケーションを閉じると、実際のメモリに問題がないので、私はほとんどメモリを使います。私はednにすべてを書き込んで、ドローロード時間なしで遅延的にデータにアクセスするようにしました。 –

+0

あなたのコメントはたくさん触れていますので、私は答えにファンダメンタルズを追加し、必要に応じて後で詳細に触れることができます。 –

+0

答えをありがとう、私はあなたのソリューションを試して、それも働いている。それは計算時間がより速いように思えるので、より良いかもしれませんが、私はある時点で最大のメモリにいます。それがうまくいくと問題はありません。本当に問題のあるのはMS SQLのクエリだと思います。HDDに書き込むことで、私はline-seqで読みやすくなりました。私はClojureが最後のオブジェクトをメモリに保持するという印象を持っていますが、バインドされていないにもかかわらず、メモリがきれいになりました。私はサンプルコード –

関連する問題