Iterátory C# vs F#
Již jsem tu na blogu trochu kritizoval F# za nedostatečně optimální kód. Ted bych rád byl více konkrétní. Na pomoc jsem zavolal CLRProfiler. Napsal jsem dva “identické” programy. Nejdříve F# verzi:
1 #light
2 open System
3 open System.IO
4
5 let mutable fileCount = 0
6 let mutable dirCount = 0
7
8 let rec allFiles dir =
9 seq {
10 for file in Directory.GetFiles dir do
11 fileCount <- fileCount + 1
12 yield file
13 for subdir in Directory.GetDirectories dir do
14 dirCount <- dirCount + 1
15 yield! (allFiles subdir)
16 }
17
18 for i in allFiles @"C:\Windows\System32\" do Console.WriteLine i
19 printf "Files: %d Dirs: %d" fileCount dirCount
Krátké, výstižné, nedebugovatelné (ale to počítám, že snad s dalšími verzemi F# vyřeší) …
A pokračuje C# verze:
1 using System;
2 using System.IO;
3 using System.Collections.Generic;
4
5 namespace Test
6 {
7 class Program
8 {
9 static int fileCount = 0;
10 static int dirCount = 0;
11
12 static IEnumerable<string> allFiles(string aDir)
13 {
14 foreach (var file in Directory.GetFiles(aDir))
15 {
16 fileCount++;
17 yield return file;
18 }
19 foreach (var subdir in Directory.GetDirectories(aDir))
20 {
21 dirCount++;
22 foreach (var file in allFiles(subdir))
23 {
24 yield return file;
25 }
26 }
27 }
28
29 static void Main(string[] aArgs)
30 {
31 foreach (var i in allFiles(@"C:\Windows\System32\")) Console.WriteLine(i);
32 Console.WriteLine("Files: {0} Dirs: {1}", fileCount, dirCount);
33 }
34 }
35 }
Delší (hlavně o závorky a chybějící “yield foreach”), ale taky myslím výstižné, bez problému debugovatelné.
Na mém počítači vypíše poslední WriteLine toto: (samozřejmě obě verze mají naprosto stejný výstup)
Files: 4924 Dirs: 243
No a teď výsledky z CLRProfileru: (zakroužkoval jsem objekty co jsou “navíc”, povšimněte si taky velikosti scrollbaru)
Takže už víte co jsem myslel tím větším tlakem na GC. V tomto konkrétním případě s relativně dlouhými řetězci, to dělá o 30% hůře pro F# (112% v počtu instancí!).
PS: Vývojáři F#, ale mají u mne jedno malé plus za to, že #light režim bude výchozí nastavení. Začínám vidět výhody velmi kontroverzního Python like scope==indent :-) (Především vynucená disciplína alespoň nějakého vzhledu zdrojáků)