StringBuilder到FileStream与直接FileStream性能

本文关键字:FileStream 性能 StringBuilder | 更新日期: 2023-09-27 18:00:15

我正在向FileStream增量写入大量文本数据,而且速度相当慢。如果我改为将文本写入内存中的StringBuilder,然后将StringBuilder批量转储到FileStream,会更快吗?我认为这样做可能可以利用FileStream中的某种缓冲,但我对FileStream的工作方式了解不够,无法进行判断。

StringBuilder到FileStream与直接FileStream性能

由于我们没有您的序列化代码,并且您怀疑您可以通过使用优化的Stream和/或StringBuilder来获得性能提升,我确实在LinqPad中设置了一个测试装置,以建立一个具有长字符串属性和其他一些属性的类的列表。该列表已序列化到磁盘
Xml序列化后的磁盘大小为115.910.381字节(110 MB)。

试验台

void Main()
{
    var list = new List<Test>();
    for(int k=0;k<100;k++) list.Add(
        new Test { Prop1 = Rnd(), /* random string of 1 MB */
                 Prop2 =k, Prop3=k*k, Prop4= DateTime.Now});
    BinaryFormatter(list);
    DataContractJsonSerializer(list);
    XmlSerializer(list);
    XmlSerializerBuffered(list);
    XmlSerializerMemory(list);
    XmlSerializerStringBuilder(list);
}

由于Xml序列化程序花费了最多的时间,我决定只在该变体中尝试不同的技术。

直接文件流

void XmlSerializer(List<Test> list)
{
    var sw = new Stopwatch();
    sw.Start();
    var s = new FileStream("c:''temp''test.xml", FileMode.Create);
    var x = new XmlSerializer(typeof(List<Test>));
    x.Serialize(s,list);
    s.Close();
    sw.Stop();
    
    sw.Elapsed.Dump("Xml");
}

缓冲流

void XmlSerializerBuffered(List<Test> list)
{
    var sw = new Stopwatch();
    sw.Start();
    var s = new FileStream("c:''temp''test.xmlbuf", FileMode.Create);
    var b = new BufferedStream(s);
    var x = new XmlSerializer(typeof(List<Test>));
    x.Serialize(b,list);
    b.Close();
    s.Close();
    sw.Stop();
    
    sw.Elapsed.Dump("Xml Buffered");
}

首先在MemoryStream中,然后复制

void XmlSerializerMemory(List<Test> list)
{
    var sw = new Stopwatch();
    sw.Start();
    var s = new FileStream("c:''temp''test.xmlmem", FileMode.Create);
    var m = new MemoryStream(1024*1024);  // INITIAL BUFFER SIZE (can and will grow!)
    // also works but is slower: var m = new MemoryStream();     
   var x = new XmlSerializer(typeof(List<Test>));
    x.Serialize(m,list);
    
    m.Position=0;
    m.CopyTo(s);
    m.Close();
    s.Close();
    sw.Stop();
    
    sw.Elapsed.Dump("Xml Mem");
}

StringBuilder

void XmlSerializerStringBuilder(List<Test> list)
{
    var sw = new Stopwatch();
    sw.Start();
    var s = new StreamWriter("c:''temp''test.xmlsb");
    var sb = new StringBuilder();
    var m = new StringWriter(sb);
    var x = new XmlSerializer(typeof(List<Test>));
    x.Serialize(m,list);
    s.Write(sb.ToString()); // http://stackoverflow.com/a/5027483/578411
    s.Close();
    sw.Stop();
    sw.Elapsed.Dump("Xml StringBuilder");
}

我的结果(在Win7(64位)/.Net 4.0/x86/4GB/RAID 0+1上)

一个典型的结果是这样的:

Xml                00:00:01.5116768 
Xml Buffered       00:00:01.3149263 
Xml Mem            00:00:01.2465760 
Xml StringBuilder  00:00:02.1440784 

所有数据首先写入内存流,然后一次性复制到流的变体总是最快的
StringBuilder总是最慢的,但XmlSerializer中没有重载可以直接"写入"StrigBuilder。因此,使用StringWriter作为额外的间接寻址,这需要时间。

现在请记住,这只是一个丑陋的未优化测试代码,只是为了了解哪种可能有效。仅根据您的数据设置中的实际性能数据进行优化。一次换一件东西,然后继续测量。

数据类

[Serializable]
public class Test
{
     public string Prop1 {get; set;}
     public int Prop2 {get;set;}
     public double Prop3 {get;set;}
     public DateTime Prop4 {get;set;}
     
}

StringBuilder可以帮助您减少创建的字符串数量。如果您在文件流中写入了许多独特的字符串,这可能会产生一些实际的开销。与您讨论字符串连接时的讨论相同(请参阅连接字符串的最有效方法?)。在这种情况下,您认为糟糕的性能可能不是FileStream。

最好的做法是什么?运行性能分析工具。