2014-01-08 21 views
5

Ich möchte große XML-Dateien zu Testzwecken generieren, aber der Code, mit dem ich endete, ist wirklich langsam, die Zeit wächst exponentiell mit der Anzahl der Zeilen, die ich in die Datei schreibe. Das folgende Beispiel zeigt, dass es Millisekunden dauert, 100 Zeilen zu schreiben, aber es dauert mehr als 20 Sekunden, um 1000 Zeilen zu schreiben (auf meinem Rechner). Ich kann wirklich nicht herausfinden, was das langsam macht, da ich denke, dass das Schreiben von 1000 Zeilen nicht so lange dauern sollte. Außerdem dauert das Schreiben von 200 Zeilen etwa 4 mal so lange wie das Schreiben von 100 Zeilen, was nicht gut ist. Um den Code auszuführen, müssen Sie möglicherweise den Pfad für die StreamWriter ändern.Wie schreibe ich effektiv in eine Datei in F #?

open System.IO 
open System.Diagnostics 

let xmlSeq = Seq.initInfinite (fun index -> sprintf "<author><name>name%d</name><age>%d</age><books><book>book%d</book></books></author>" index index index) 

let createFile (seq: string seq) numberToTake fileName = 
    use streamWriter = new StreamWriter("C:\\tmp\\FSharpXmlTest\\FSharpXmlTest\\" + fileName, false) 
    streamWriter.WriteLine("<startTag>") 
    let rec internalWriter (seq: string seq) (sw:StreamWriter) i (endTag:string) = 
     match i with 
     | 0 -> (sw.WriteLine(Seq.head seq); 
      sw.WriteLine(endTag)) 
     | _ -> (sw.WriteLine(Seq.head seq); 
      internalWriter (Seq.skip 1 seq) sw (i-1) endTag) 
    internalWriter seq streamWriter numberToTake "</startTag>" 

let funcTimer fn = 
    let stopWatch = Stopwatch.StartNew() 
    printfn "Timing started" 
    fn() 
    stopWatch.Stop() 
    printfn "Time elased: %A" stopWatch.Elapsed 


(funcTimer (fun() -> createFile xmlSeq 100 "file100.xml")) 
(funcTimer (fun() -> createFile xmlSeq 1000 "file1000.xml")) 

Antwort

5

Sie beobachteten ein quadratisches Verhalten O(n^2) bei der Manipulation von Sequenzen. Wenn Sie Seq.skip aufrufen, wird eine neue Sequenz erstellt, so dass Sie den verbleibenden Teil implizit durchlaufen. Eine detailliertere Erklärung findet sich unter https://stackoverflow.com/a/1306267.

In diesem Beispiel müssen Sie keine Sequenzen zerlegen. Ersetzen Sie Ihre innere Funktion durch:

Ich kann 10000 Zeilen in Bruchteil einer Sekunde schreiben.

Sie können weiter refaktorieren, indem Sie diese innere Funktion entfernen und ihren Körper in die Elternfunktion kopieren.

Wie oben erwähnt, sollte LazyList besser zu verwenden sein, wenn Sie jemals Sequenzen zerlegen möchten.

+0

Vielen Dank für die Klärung, dass ich jedes Mal eine neue Sequenz erstellt habe. Allerdings sollte ich die innere Funktion ersetzen, ich könnte einfach die innere Funktion entfernen und die for-Schleife zur äußeren Funktion hinzufügen. –

2

pad in seiner Antwort hat auf die Ursache der Verlangsamung hingewiesen. Ein weiterer idiomatische Ansatz könnte statt unendliche Folge sein der benötigten Länge zu erzeugen Sequenz mit Seq.unfold, die der Code wirklich trivial macht:

let xmlSeq n = Seq.unfold (fun i -> 
    if i = 0 then None 
    else Some((sprintf "<author><name>name%d</name><age>%d</age><books><book>book%d</book></books></author>" i i i), i - 1)) n 

let createFile seqLen fileName = 
    use streamWriter = new StreamWriter("C:\\tmp\\FSharpXmlTest\\" + fileName, false) 
    streamWriter.WriteLine("<startTag>") 
    seqLen |> xmlSeq |> Seq.iter streamWriter.WriteLine 
    streamWriter.WriteLine("</startTag>") 

(funcTimer (fun() -> createFile 10000 "file10000.xml")) 

generieren 10000 Elemente dauert etwa 500 ms auf meinem Laptop.

+0

Vielen Dank für das Ausfüllen anderer nützlicher Informationen. Die anderen Antworten sind gezielter meine Frage, aber Sie haben einige wirklich nützliche zusätzliche Informationen präsentiert. –

Verwandte Themen