gpt4 book ai didi

c# - F# 与 C# 性能签名以及示例代码

转载 作者:太空狗 更新时间:2023-10-29 20:06:04 26 4
gpt4 key购买 nike

已经有很多关于这个话题的讨论,但我只是想鞭打死马,尤其是当我发现他们可能还在呼吸的时候。

我一直致力于解析 CSV 这种不寻常且奇特的文件格式,为了好玩,我决定针对我所知道的 2 种 .net 语言(C# 和 F#)来表征性能。

结果……令人不安。 F# 以 2 倍或更多的优势获胜(实际上我认为它更像是 .5n,但事实证明获得真正的基准测试非常困难,因为我正在针对硬件 IO 进行测试)。

在像读取 CSV 这样常见的事情中,不同的性能特征让我感到惊讶(请注意,系数意味着 C# 在非常小的文件上获胜。我进行的测试越多,我就越觉得 C# 的规模更差,这既是令人惊讶和担忧,因为这可能意味着我做错了)。

一些注意事项:Core 2 duo 笔记本电脑,主轴磁盘 80 gig,3 gig ddr 800 内存,windows 7 64 位高级版,.Net 4,未打开电源选项。

30,000 行 5 宽 1 个短语 10 个字符或更少是我在第一次运行后支持尾调用递归的 3 倍(它似乎缓存了文件)

300,000(重复相同的数据)是尾调用递归的 2 倍,F# 的可变实现略胜一筹,但性能特征表明我正在访问磁盘而不是 ram-disking 整个文件,这会导致半-随机性能峰值。

F#代码

//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#代码

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;
}
}

}

请注意那里的各种实现。在 2 种语言中使用迭代器、使用序列、使用尾调用优化、while 循环...

一个主要问题是我正在访问磁盘,因此可以解释一些特殊情况,我打算重写这段代码以从内存流中读取(假设我不开始,这应该更一致交换)

但我所学/阅读的所有内容都说 while 循环/for 循环比尾调用优化/递归更快,而我运行的每个实际基准测试都在说完全相反的事情。

所以我想我的问题是,我应该质疑传统智慧吗?

尾调用递归真的比 .net 生态系统中的 while 循环更好吗?

这在 Mono 上如何实现?

最佳答案

我认为差异可能源于 F# 和 C# 中的不同 List。 F# 使用单链表(参见 http://msdn.microsoft.com/en-us/library/dd233224.aspx ),而在 C# 中使用 System.Collections.Generic.List ist,它基于数组。

单向链表的串联要快得多,尤其是在解析大文件时(您需要不时分配/复制整个数组列表)。

尝试在 C# 代码中使用 LinkedList,我对结果很好奇 :) ...

PS:此外,这将是一个关于何时使用分析器的好例子。您可以轻松找到 C# 代码的“热点”...

编辑

所以,我自己尝试了这个:我使用了两个相同的文件来防止缓存效应。这些文件有 3.000.000 行,包含 10 次“abcdef”,以逗号分隔。

主程序如下所示:

static void Main(string[] args) {
var dt = DateTime.Now;
CSVprocess.ImportDataC("test.csv"); // C# implementation
System.Console.WriteLine("Time {0}", DateTime.Now - dt);
dt = DateTime.Now;
CSVImport.ImportData("test1.csv"); // F# implementation
System.Console.WriteLine("Time {0}", DateTime.Now - dt);
}

(我也尝试过先执行 F# 实现,然后再执行 C#...)

结果是:

  • C#:3.7 秒
  • F#:7.6 秒

在 F# 解决方案之后运行 C# 解决方案为 F# 版本提供了相同的性能,但 C# 为 4.7 秒(我假设是由于 F# 解决方案分配了大量内存)。单独运行每个解决方案不会改变上述结果。

使用具有 6.000.000 行的文件为 C# 解决方案提供了大约 7 秒的时间,F# 解决方案产生了 OutOfMemoryException(我在具有 12GB Ram 的机器上运行它......)

所以对我来说,传统的“智慧”似乎是正确的,使用简单循环的 C# 对于此类任务来说更快......

关于c# - F# 与 C# 性能签名以及示例代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4872429/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com