首先在 StringBuilder 中存储的 File.AppendText 或 File.WriteAllText 消耗的资源更少,速度更快?

Posted

技术标签:

【中文标题】首先在 StringBuilder 中存储的 File.AppendText 或 File.WriteAllText 消耗的资源更少,速度更快?【英文标题】:What consumes less resources and is faster File.AppendText or File.WriteAllText storing first in StringBuilder? 【发布时间】:2013-04-17 23:32:02 【问题描述】:

我必须将数千个动态生成的行写入文本文件。 我有两个选择,哪个消耗资源少,速度快?

A.使用 StringBuilder 和 File.WriteAllText

StringBuilder sb = new StringBuilder();

foreach(Data dataItem in Datas)

    sb.AppendLine(
        String.Format(
            "0, 1-2",
            dataItem.Property1,
            dataItem.Property2,
            dataItem.Property3));


File.WriteAllText("C:\\example.txt", sb.ToString(), new UTF8Encoding(false)); 

B.使用 File.AppendText

using(StreamWriter sw = File.AppendText("C:\\example.txt"))

    foreach (Data dataItem in Datas)
    
        sw.WriteLine(
            String.Format(
                "0, 1-2",
                dataItem.Property1,
                dataItem.Property2,
                dataItem.Property3));
    

【问题讨论】:

ericlippert.com/2012/12/17/performance-rant 我需要尽快完成这个操作,因为涉及到网络和数据库写入。而且我有一些相关的瓶颈,所以我问这个问题是因为我需要,而不是因为我不知道你链接的文章。 我不确定速度是否应该是您主要关心的问题。内存使用显然是推动您做出决定的因素。如果数据总是很小,那么您可以运行一些测试来确定哪个更快。 【参考方案1】:

您的第一个版本将所有内容放入StringBuilder 然后写入它,将消耗最多的内存。如果文本非常大,则可能会耗尽内存。它有可能更快,但也可能更慢。

第二个选项将使用更少的内存(基本上,只是StreamWriter 缓冲区),并且性能非常好。我会推荐这个选项。它的性能很好 - 可能比第一种方法更好 - 并且没有同样的内存耗尽可能性。

您可以通过增加输出缓冲区的大小来大大加快它的速度。而不是

File.AppendText("filename")

使用以下命令创建流:

const int BufferSize = 65536;  // 64 Kilobytes
StreamWriter sw = new StreamWriter("filename", true, Encoding.UTF8, BufferSize);

64K 的缓冲区大小比默认的 4K 缓冲区大小提供更好的性能。您可以更大,但我发现大于 64K 的性能提升很小,而且在某些系统上实际上会降低性能。

【讨论】:

这个缓冲区大小很难预料,因为我不知道需要多少行和数据。我认为在2k和4k之间。如果我任意选择一个小于我需要的缓冲区大小,我就有 OutOfMemoryException 的风险。 @AlbertoLeón:不,选择一个太小的缓冲区大小不会给你一个内存不足的异常。这只是StreamWriter 用来防止它不得不为每个字符调用Windows Write 函数的临时缓冲区。它缓冲数据,然后将其写入块中。如果您尝试写入大于缓冲区的块,则不会出现错误。 太棒了!然后我认为你的解决方案改进了我的第二种方法,我喜欢它。 "我发现大于 64K 的文件性能提升很小,而且在某些系统上实际上会降低性能"。我有兴趣知道,如何?是通过显式基准测试还是通过应用程序中的真实数据隐式进行? @user1451111 我没有进行严格的基准测试。我确实以不同的缓冲区大小测试了我的几个应用程序。自从我收集了答案所依据的数据以来,.NET Framework 发生了很多变化。现在情况可能大不相同了。【参考方案2】:

您至少还有一个选择,使用File.AppendAllLines()

var data = from item in Datas
            select string.Format("0, 1-2", item.Property1, item.Property2, item.Property3);

File.AppendAllLines("Filename", data, new UTF8Encoding(false));

理论上,这将比您的第一种方法使用更少的内存,因为一次只会在内存中缓冲一行。

不过,它可能与您的第二种方法几乎完全相同。我只是向你展示第三种选择。这个唯一的好处是你可以给它一个 Linq 序列,这有时很有用。

I/O 速度将使其他任何考虑都相形见绌,因此您应该像上面提到的 juharr 那样专注于最小化内存使用(当然还要考虑过早优化的危险!)

这意味着使用你的第二种方法,或者我放在这里的那个。

【讨论】:

我觉得我最喜欢这个。很好的答案。 我很惊讶,因为行数减少了。更易读。但是什么是 var 数据?是字符串数组吗? @AlbertoLeón: dataIEnumerable<string>。您需要阅读 LINQ 以了解其后果。 不喜欢 LINQ 的特殊语法,喜欢函数风格的朋友,var data = Datas.Select(item => string.Format(...));

以上是关于首先在 StringBuilder 中存储的 File.AppendText 或 File.WriteAllText 消耗的资源更少,速度更快?的主要内容,如果未能解决你的问题,请参考以下文章

StringBuilder线程为什么不安全

JAVA之旅(十七)——StringBuffer的概述,存储,删除,获取,修改,反转,将缓存区的数据存储到数组中,StringBuilder

学习笔记——stringbuffer,string和stringbuilder

stringbuffer和stringbuilder

StringBuffer和StringBuilder类

C# string 和 stringbuilder