首先在 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:data
是 IEnumerable<string>
。您需要阅读 LINQ 以了解其后果。
不喜欢 LINQ 的特殊语法,喜欢函数风格的朋友,var data = Datas.Select(item => string.Format(...));
以上是关于首先在 StringBuilder 中存储的 File.AppendText 或 File.WriteAllText 消耗的资源更少,速度更快?的主要内容,如果未能解决你的问题,请参考以下文章
JAVA之旅(十七)——StringBuffer的概述,存储,删除,获取,修改,反转,将缓存区的数据存储到数组中,StringBuilder