jsonconvert.serialize f#中的性能



i创建记录类型如下:

type CombEmp =
    {
        empid:int
        empname:string
        email:string                       
    }
let defCombEmp:CombEmp =
    {
        empid = 0
        empname = ""
        email = ""
    }

i然后创建记录实例:

let chrE1 = {defCombEmp with empid = 100; empname = "Wayne Rooney"; email = "wroo@mun.com"}
let chrE2 = {defCombEmp with empid = 100; empname = "Wayne R"; email = "war@mun.com"}
let chrE3 = {defCombEmp with empid = 100; empname = "Wayne R"; email = "rooney@mun.com"}    

然后,我使用上述实例创建了一个65-70记录的列表:

let hrLst = [|chrE1;chrE2;chrE3;chrE1;chrE2;chrE3;chrE1; ...... |] |> Array.toList

我现在已经编写了如下所示。

功能GetByteCount获取序列化数据的大小(在此处使用newtonsoft)。

该功能loop1使用上述hrLst创建了约500K长的长列表。

函数loop2在每次迭代中都将输入序列降低1000,并将其剩余列表调用GetByteCount

let GetByteCount data = //can improve this algorithm?
    let stopWatch = System.Diagnostics.Stopwatch.StartNew()
    let x = data
            |> JsonConvert.SerializeObject
            |> Encoding.UTF8.GetByteCount
    stopWatch.Stop()
    Console.WriteLine("time reqd: " + stopWatch.Elapsed.TotalMilliseconds.ToString() + " milliseconds")
    x
let PerfTestLoop() =
    let rec loop1 ctr l =
        if ctr%100 = 0 then Console.WriteLine("" + ctr.ToString())
        match ctr with
        |500 -> l
        |_ ->
            let l2 = l @ hrLst
            loop1 (ctr+1) l2
    let l = loop1 1 hrLst
    let len = l.Length
    let s = l @ l |> List.toSeq
    Console.WriteLine("input length: " + (Seq.length s).ToString())
    Console.WriteLine("Start Measuring ..")
    let stopWatch = System.Diagnostics.Stopwatch.StartNew()
    let rec loop2 ctr s =
        match ctr with
        |100 -> s |> GetByteCount
        |_ -> 
            let news = Seq.skip 2000 s        
            let size = news |> GetByteCount
            loop2 (ctr+1) news
    let x = loop2 1 s
let res =  PerfTestLoop() // becomes slow gradually

观察是,即使序列的大小正在减小,在loop2的每次迭代中执行GetByteCount的执行时间都会增加!为什么会发生这种情况?在任务管理器中,CPU和内存使用率保持稳定。是否有其他方法可以找到数据的字节数或减少执行GetByteCount所需的时间?

如果在loop2中i删除Seq.skip行并在每个迭代中使用相同的序列,则每次迭代所需的时间相似,并且不会变化太大。

此代码有许多可能的改进,但是这里的主要问题不是根据其语义和性能中的相对优点和缺点来理解何时选择列表,数组或SEQ。/p>

f#列表是一个不变的单链接列表,它快速添加和从正面添加和删除项目很快。在串联时(@运算符)或以某个索引访问项目。

f#数组是.NET数组:它是可变的,并且具有快速的索引访问等。但是,大多数用于使用数组的F#功能都会避免突变并制作副本。

seq是.NET IEnumerable<T>。这是您可以从头开始列举的序列。这些值是根据序列消费者需要计算的,并且计算可能很昂贵。每个值可能取决于先前的值,具体取决于seq的实现,这就是为什么如果您创建具有跳过值的新序列并将其传递给其他地方,那么新的呼叫站点仍然需要重新评估跳过的值以获取以获取它实际使用的。在F#中解决此问题的一种方法是使用Seq.cache。这将使正常的SEQ变成一个评估时缓存每个项目的SEQ,以免重复访问不会引起重计。


那么,为什么首先需要使用seq?您需要一个list,这并不懒惰,因此您将所有这些数据都藏在内存中。您做let s = l @ l |> List.toSeq。将其更改为List.toArray,然后在以后使用Array.skip将其更快地更快。

,这也可能有助于将所有列表更改为数组,因为您不利用列表的好处。如果将hrLst作为数组保持,则可以完全删除loop1,然后执行此操作:Array.replicate 500 hrLst |> Array.concat


通常,我建议您对F#集合非常熟悉:列表,数组,SEQ,MAP和SET。浏览这些模块中的功能及其文档,因为它们几乎在所有F#代码中都会有用。内置功能通常会消除使用递归的需求,并导致更简单的代码。我怀疑loop2也可以用Array.chunkBySize的某些使用来代替,但我不清楚您的代码要做什么。

相关内容

  • 没有找到相关文章

最新更新