5

私は algo-class.org コースに従っており、そのプログラミング割り当ての 1 つで、次のような形式のファイルが提供されます。

1 2
1 5
2 535

...

このような行は 500 万以上あります。ファイルを読み込んで、[[1 2][1 5][2 535]...] のような整数ベクトルのベクトルに変換したいと考えています。

(defn to-int-vector [s]
    (vec (map #(Integer/parseInt %) (re-seq #"\w+" s))))    

(def ints (with-open [rdr (clojure.java.io/reader "<file>")]
               (doall (map to-int-vector (line-seq rdr)))))

したがって、私はこのように信じています。ファイル全体をメモリに保持しておらず、大きな整数ベクトルのみを生成しています。しかし、これから OutOfMemoryError を取得します。rand-int を実行して、同じサイズと同じ形式のベクトルを生成しようとしましたが、うまくいきました。

生成された一時オブジェクトが原因でメモリの問題が発生しているように見えますか? このようなケースを処理するための clojure の理想的な方法は何ですか?

アップデート:

はい、整数ベクトル全体を保持していることに気付きました。ヒープサイズを上げたところ、動作するようになりました。ベクトルと 500 万の要素 (1000 万の整数) が非常に多くのメモリを占有する可能性があることに興味があります。jvm に 3g を割り当てる必要があります。メモリをダウンさせる他の方法はありますか?

4

3 に答える 3

5

実現されたレイジー seq がどれだけのオーバーヘッドを課すか、信じられないでしょう。これを 64 ビット OS でテストしました。120 バイト程度です。これは、すべてのレイジー seq メンバーの純粋なオーバーヘッドです。一方、ベクトルはオーバーヘッドが非常に低く、ベクトルが十分に大きい場合、基本的に Java 配列と同じです。で置き換えdoallてみてくださいvec

また、オーバーヘッドなしでどれだけのメモリを消費しているかを見てみましょう。5e6 ペアの整数があります。つまり、5e6 x 8 = 40 MB です。short を使用して節約でき、50% 節約できます (繰り返しますが、これは親コレクションのオーバーヘッドをカウントしていません。ペアを保持する各ベクター インスタンスには独自のオーバーヘッドがあります)。

保存の次のステップは、外部コレクションとペアの両方に未加工の配列を使用することです。配列はシーケンス可能であり、言語と非常によく統合されるため、これは依然として非常に実用的なソリューションになる可能性があります。vecこれを行うには、 の 2 つのオカレンスをに置き換えるだけですto-array

アップデート

との違いはIntegerShortどちらもまだ本格的なオブジェクトであるため、それほど大きくありません。の代わりにshort-array(または)を使用して、数値のペアをプリミティブ配列として格納すると、さらに節約できます。int-arrayto-array

于 2012-04-11T07:51:17.127 に答える
2

with-open怠惰を利用すると同時にカプセル化するのは難しいです。怠惰は、ラインまたはint-vectorシーケンスの「関連する」部分のみをメモリに含めることができるため、この場合は重要です。

この問題の解決策の1つは、カプセル化せず、フォームwith-openの動的スコープ内に行処理ロジック全体を含めることです。with-open

(with-open [rdr (clojure.java.io/reader "<file>")]
  (doseq [int-vector (map to-int-vector (line-seq rdr))]
    (process int-vector)))

ここでの2つの重要な詳細は、行とint-vectorシーケンスを格納しないことと、with-openフォーム内でのみ使用することです。これらの詳細により、すでに処理されたシーケンスの部分をガベージコレクションできるようになり、処理全体を通してファイルストリームが開いたままになります。

于 2012-04-11T17:48:43.687 に答える
1

definは、 各行の数値がコレクション(この場合はvec)にも格納されるため、(def ints結果全体が少なくともファイルと同じ大きさのメモリに格納されることを保証します。

また、デフォルトでは、Javaはコンピュータのすべてのメモリの使用を拒否します。場合によっては、maxHeapSizeパラメータを設定する必要があります。

新しいrepl(まだ大きなリストを保持していない)から始めた場合でも、メモリが不足していませんか?

于 2012-04-11T02:48:24.710 に答える