2015-12-11 132 views
1

我想將大型記錄(> 10,000,000個元素)數組保存到磁盤,以便稍後將數組重新加載到內存中。我用下面簡單的函數從Visual F#2010技術計算:如何將大型F#數組記錄保存到文件?

let save filename x = 
    use stream = new FileStream(filename, FileMode.Create) 
    BinaryFormatter().Serialize(stream, x) 

type Test = { a : int; b : int} 

let x = [| for i in 1..6 do 
      let a=i 
      let b=i*i 
      yield {a=a;b=b}|] 

save "file.dat" x 

當我做到這一點(與真實數據),我得到的錯誤:

System.Runtime.Serialization.SerializationException: The internal array cannot expand to greater than Int32.MaxValue elements. 

現在,我的解決辦法是轉換爲Deedle,然後保存爲csv,但我認爲保存/重新加載的計算更有效,不需要從csv重建數組。

let x2 = x |> Frame.ofRecords 
x2.SaveCsv("file.csv") 
+0

我要麼自己生成CSV,要麼嘗試使用FsPicker:http://nessos.github.io/FsPickler/ –

+0

'Int32.MaxValue'是'2,147,483,647',這個數量級比' 10,000,000',所以我想知道這裏真的出了什麼問題...... –

+0

@MarkSeemann也許我說的大小錯了 - 我是以x.Length爲基礎的。記錄的類型是'type rp = {a:int; b:int; c:LocalDate; d:LocalDate; c:float; d:float}'是否重要?數組是'val rp:rp []'。我認爲這些關於記錄類型的細節是無關緊要的。 – nh2

回答

2

將10,000,000行寫入文本文件不成問題。這裏有一個簡單的演示:

> let lines = Seq.initInfinite (fun i -> sprintf "%i, %i, -%i" i (i * 2) i);; 

val lines : seq<string> 

> open System.IO;; 
> #time;; 

--> Timing now on 

> File.WriteAllLines(@"test.csv", lines |> Seq.take 10000000);; 
Real: 00:00:20.420, CPU: 00:00:20.343, GC gen0: 3528, gen1: 3, gen2: 1 
val it : unit =() 

正如你所看到的,這隻需要20秒。

讀線後面是不是也不錯:

> let roundTripped = File.ReadLines @"test.csv";; 
Real: 00:00:00.000, CPU: 00:00:00.000, GC gen0: 0, gen1: 0, gen2: 0 

val roundTripped : System.Collections.Generic.IEnumerable<string> 

正如你所看到的,這種情況發生在瞬間,因爲roundTripped加載爲懶洋洋地評估序列。

不過,這是可能的枚舉值:

> roundTripped |> Seq.iter (printfn "%s") 

(打印截爲清楚起見,字面上有10萬。)

... 
9999997, 19999994, -9999997 
9999998, 19999996, -9999998 
9999999, 19999998, -9999999 
Real: 00:03:43.995, CPU: 00:01:15.390, GC gen0: 594, gen1: 23, gen2: 3 
val it : unit =() 

這需要花費很多時間,但我懷疑這主要是因爲打印到控制檯往往需要時間。

這些實驗是在我3歲的聯想X1碳 - 一個相當主流的硬件上完成的。

因此,寫入或讀取數百萬條文本行沒有問題,但請注意,我避免使用數組來支持懶散評估的序列。


使用記錄不會改變上述結論。我不敢在.NET序列化中設計任何類型的持久化持久性解決方案(由於潛在的版本問題),所以我仍然爲此轉換爲其他格式。

堅持的CSV:

type Test = { A : int; B : int } 

let records = Seq.initInfinite (fun i -> { A = i; B = -i }) 
let csvs = records |> Seq.map (fun x -> sprintf "%i, %i" x.A x.B) 

記錄可以寫成如上述報道中讀出大致相同的時間。

+0

你能改變你的答案來使用記錄嗎?我試圖做一個簡單的編輯來使用相同的代碼,但是與記錄中的問題一樣,但它被拒絕了。將有助於新用戶搜索如何將記錄數組保存到磁盤。 – nh2

+0

@ nh2添加了幾條關於記錄的段落 –

+0

感謝關於版本控制的觀點。我不知道那件事。 – nh2

相關問題