Ya hay muchas discusiones sobre este tema, pero me refiero a azotar caballos muertos, particularmente cuando descubro que todavía pueden estar respirando.F # vs. C# performance Firmas con código de ejemplo
Estaba trabajando en el análisis del formato de archivo inusual y exótico que es el CSV, y por diversión decidí caracterizar el rendimiento frente a los 2 lenguajes .net que conozco, C# y F #.
Los resultados fueron ... inquietantes. F # ganó, por un amplio margen, un factor de 2 o más (y de hecho creo que se parece más a .5n, pero obtener referencias reales está resultando difícil ya que estoy probando contra hardware IO).
Las características de rendimiento divergente en algo tan común como leer un CSV me resultan sorprendentes (tenga en cuenta que el coeficiente significa que C# gana en archivos muy pequeños. Cuantas más pruebas hago, más se siente que C# empeora, lo cual ambos sorprendentes y preocupantes, ya que probablemente significa que lo estoy haciendo mal).
Algunas notas: Core 2 duo laptop, disco de husillo 80 gigas, 3 gigas ddr 800 de memoria, Windows 7 64 bit premium, .Net 4, no hay opciones de energía activadas.
30.000 líneas 5 de ancho 1 frase 10 caracteres o menos me da un factor de 3 a favor de la recursividad llamada de cola después de la primera carrera (que aparece en caché el archivo)
300.000 (mismos datos repetidos) es un factor de 2 para la recursión de llamadas de cola con la implementación mutable de F # ganando un poco, pero las firmas de rendimiento sugieren que estoy golpeando el disco y no destruyendo todo el archivo, lo que causa picos de rendimiento semi aleatorios.
F # Código
//Module used to import data from an arbitrary CSV source
module CSVImport
open System.IO
//imports the data froma path into a list of strings and an associated value
let ImportData (path:string) : List<string []> =
//recursively rips through the file grabbing a line and adding it to the
let rec readline (reader:StreamReader) (lines:List<string []>) : List<string []> =
let line = reader.ReadLine()
match line with
| null -> lines
| _ -> readline reader (line.Split(',')::lines)
//grab a file and open it, then return the parsed data
use chaosfile = new StreamReader(path)
readline chaosfile []
//a recreation of the above function using a while loop
let ImportDataWhile (path:string) : list<string []> =
use chaosfile = new StreamReader(path)
//values ina loop construct must be mutable
let mutable retval = []
//loop
while chaosfile.EndOfStream <> true do
retval <- chaosfile.ReadLine().Split(',')::retval
//return retval by just declaring it
retval
let CSVlines (path:string) : string seq=
seq { use streamreader = new StreamReader(path)
while not streamreader.EndOfStream do
yield streamreader.ReadLine() }
let ImportDataSeq (path:string) : string [] list =
let mutable retval = []
let sequencer = CSVlines path
for line in sequencer do
retval <- line.Split()::retval
retval
C# Código
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
namespace CSVparse
{
public class CSVprocess
{
public static List<string[]> ImportDataC(string path)
{
List<string[]> retval = new List<string[]>();
using(StreamReader readfile = new StreamReader(path))
{
string line = readfile.ReadLine();
while (line != null)
{
retval.Add(line.Split());
line = readfile.ReadLine();
}
}
return retval;
}
public static List<string[]> ImportDataReadLines(string path)
{
List<string[]> retval = new List<string[]>();
IEnumerable<string> toparse = File.ReadLines(path);
foreach (string split in toparse)
{
retval.Add(split.Split());
}
return retval;
}
}
}
nota la variedad de implementaciones allí. Usando iteradores, usando secuencias, usando optimizaciones de llamadas de cola, mientras bucles en 2 idiomas ...
Un problema importante es que estoy golpeando el disco, y algunas idiosincrasias pueden explicarse por eso, tengo la intención de volver a escribir esto código para leer desde una secuencia de memoria (que debería ser más consistente asumiendo que no empiezo a intercambiar)
Pero todo lo que me enseñan/leen dice que aunque los bucles loops/for son más rápidos que las optimizaciones/recursivas de cola, y cada punto de referencia real que corro es decir lo contrario de eso.
Así que supongo que mi pregunta es, ¿debería cuestionar la sabiduría convencional?
¿La recursión de la cola de cola es realmente mejor que la de los bucles en el ecosistema .net?
¿Cómo funciona esto en Mono?
Lo probé por mí mismo y no puedo reproducir sus resultados. Mis pruebas dan como resultado que C# sea un 50% más rápido, vea mi respuesta a continuación ... – MartinStettner
resulta que los puntos de referencia estaban buscando en el lugar equivocado, esto es CPU enlazada a .split, no io bound, Y fue aproximadamente 3.5 segundos para todos de las implementaciones para leer en los datos, excepto .ReadLines que fue de aproximadamente 10 segundos. El .Split varió, pero también estuvo en general en el mismo estadio de béisbol (aproximadamente 20 segundos). – Snark