Zugegeben, ich bin mir nicht sicher, ob ich hier Äpfel mit Äpfeln oder Äpfeln richtig mit Birnen vergleiche. Aber ich bin besonders überrascht über die Größe des Unterschieds, wo ein geringerer Unterschied, wenn überhaupt, erwartet werden würde.Warum ist die Funktionszusammensetzung in F # um 60% so viel langsamer als beim Piping?
can often be expressed as function composition and vice versa Rohrleitungen, und ich würde davon ausgehen, der Compiler, dass auch weiß, so habe ich versucht, ein kleines Experiment:
// simplified example of some SB helpers:
let inline bcreate() = new StringBuilder(64)
let inline bget (sb: StringBuilder) = sb.ToString()
let inline appendf fmt (sb: StringBuilder) = Printf.kbprintf (fun() -> sb) sb fmt
let inline appends (s: string) (sb: StringBuilder) = sb.Append s
let inline appendi (i: int) (sb: StringBuilder) = sb.Append i
let inline appendb (b: bool) (sb: StringBuilder) = sb.Append b
// test function for composition, putting some garbage data in SB
let compose a =
(appends "START"
>> appendb true
>> appendi 10
>> appendi a
>> appends "0x"
>> appendi 65535
>> appendi 10
>> appends "test"
>> appends "END") (bcreate())
// test function for piping, putting the same garbage data in SB
let pipe a =
bcreate()
|> appends "START"
|> appendb true
|> appendi 10
|> appendi a
|> appends "0x"
|> appendi 65535
|> appendi 10
|> appends "test"
|> appends "END"
Testing dies in FSI (64 Bit aktiviert ist, --optimize
Fahne) ergibt:
> for i in 1 .. 500000 do compose 123 |> ignore;;
Real: 00:00:00.390, CPU: 00:00:00.390, GC gen0: 62, gen1: 1, gen2: 0
val it : unit =()
> for i in 1 .. 500000 do pipe 123 |> ignore;;
Real: 00:00:00.249, CPU: 00:00:00.249, GC gen0: 27, gen1: 0, gen2: 0
val it : unit =()
Ein kleiner Unterschied wäre verständlich, aber das ist ein Faktor 1,6 (60%) Leistungseinbußen.
Ich würde eigentlich erwarten, dass der Großteil der Arbeit in der StringBuilder
passieren wird, aber anscheinend hat der Overhead der Zusammensetzung ziemlich viel Einfluss.
Ich weiß, dass in den meisten praktischen Situationen dieser Unterschied vernachlässigbar sein wird, aber wenn Sie große formatierte Textdateien (wie Protokolldateien) wie in diesem Fall schreiben, hat es eine Auswirkung.
Ich verwende die neueste Version von F #.
Danke, sehr interessante Vergleiche. Vielleicht verwenden Sie den Server-GC und ich habe den normalen Single-Thread-GC? Ich weiß nicht, wie ich das für FSI konfigurieren soll. Ich sollte die kompilierten Versionen vergleichen. Ich weiß zu schätzen, dass zumindest auf Ihrem System die Unterschiede vernachlässigbar sind, so wie es sein sollte. – Abel
Ich verwende keine speziellen Flags für FSI außer '--optimize', das ist die von Ihnen erwähnte. Ich führe auch fsianycpu.exe, falls das wichtig ist. – Ringil
@Ringil Ich bin nicht einverstanden mit Lambdas. Ja, sie werden in nicht optimiertem Code erstellt. Aber mit Optimierungen sehe ich nur zwei Lambdas, nicht neun. Alles andere wird inline geschaltet. Ich denke, das Endergebnis sollte sein, dass der Compiler härtere Zeit hat, das Inlining im Falle der Zusammensetzung herauszufinden, als im Fall von Rohrleitungen. –