2015-12-11 45 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

相关问题