我有一个程序存储一堆包含许多double类型成员的结构实例。我经常把它们转储到一个文件中,我正在使用字符串构建器,例如:
StringBuilder builder = new StringBuilder(256);
builder.AppendFormat("{0};{1};{2};", x.A.ToString(), x.B.ToString(), x.C.ToString());
其中'x'是我的类型的实例,A,B,C是double类型的X的成员。我在每个上面调用ToString()来避免装箱。但是,这些对ToString的调用仍然在我的应用程序的上下文中分配了大量内存,我想减少这一点。我在想的是拥有一个字符数组,并将每个成员直接写入该数组,然后从该字符数组创建一个字符串并将其转储到该文件中。几个问题:
1)我想做的事情听起来合理吗?有没有人知道会有类似的东西?
2)是否已经内置了将double转换为字符数组的内容(我猜这可能会达到一些参数化的精度?)。理想情况下,想要传入我的数组和一些索引并让它开始写入那里。
我试图这样做的原因是当我的应用程序运行时减少内存中的大量峰值,因为我运行了很多实例,并且经常发现自己受到内存的限制。
干杯 一个
答案 0 :(得分:3)
文件是否需要某种文本格式?
如果没有,到目前为止,最有效的方法是使用BinaryWriter(和BinaryReader读取它们)。
有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx。
答案 1 :(得分:2)
如果可以直接写入文本文件,则可以使用steamwriter编写强类型结构。我没有测试内存使用情况,但我认为它们应该是高效的
using (var tw = new System.IO.StreamWriter("filename", true)) //(true to append to file)
{
tw.Write(x.A);
tw.Write(';');
}
如果需要stringbuilder,也可以使用:
调用强类型重载 builder.Append(x.A) //strongly typed as long as the field is a system type
.Append(';')
.Append(x.B)
.Append(';');
当然,这两种方法在实现某种循环或委托方面看起来会更好,但这与装箱逻辑不同。
编辑自定义双重写作在其他答案中发布:c# double to character array or alternative
答案 2 :(得分:1)
您应该直接写入文件流以减少内存利用率。
using(var writer = new StreamWriter(...))
{
writer.Write(x.A);
writer.Write(";");
writer.Write(x.B);
writer.Write(";");
writer.Write(x.C);
}
答案 3 :(得分:1)
您确定可能导致许多Double.ToString
号召唤导致您的记忆问题吗?应该在下一代0集合上收集每个字符串,并且.NET垃圾收集器在执行此操作时非常有效。
如果你创建的字符串超过85K,它们将在大对象堆上创建,这可能会增加应用程序所需的总内存,即使大字符串只是暂时存在(大对象堆碎片)。
您可以使用性能监视器来详细了解应用程序如何使用托管堆。你已经使用了CLRProfiler这是一个更高级的工具,所以也许你不会学到任何新东西。
StringBuilder
是在内存中构建字符串的正确类,但如果您只在内存中构建字符串以便稍后将其写入文件,则应使用StreamWriter
直接写入文件。 / p>
StringBuilder
必须扩展用于存储字符串的缓冲区,并且可以通过提前设置StringBuilder
的容量来避免这种额外开销(您已在示例代码中执行此操作)。
无论您调用什么超载将Double
格式化为StringBuilder
,调用最终都会导致Double.ToString
被调用。 StringBuilder.AppendFormat
直接格式化到缓冲区而不分配额外的格式化字符串,因此在内存使用方面StringBuilder.AppendFormat
与StringBuilder.Append
一样好,并且两个重载都将分配格式为{{1的字符串作为格式化过程的一部分。但是,Double
会将StringBuilder.AppendFormat
打包,因为它接受Double
数组。使用接受params Object[]
的{{1}}重载不会遇到此问题。
如果您确定知道StringBuilder.Append
是您的内存问题的根源,我相信您最好的选择是编写自己的浮点格式代码,该代码可以将浮点数直接写入{{1} }。这项任务非常重要,但您可以从开源C库中获取灵感。
答案 4 :(得分:1)
出于对如何去做的纯粹好奇,我无法抗拒试图创造一个直接写双打的场景。结果如下。我没有对它进行基准测试,但它确实在我运行的(有限)测试中按预期工作。
double[] test = { 8.99999999, -4, 34.567, -234.2354, 2.34, 500.8 };
using (var sw = new FileStream(@"c:\temp\test.txt", FileMode.Create))
{
using (var bw = new BinaryWriter(sw))
{
const byte semicol = 59, minus = 45, dec = 46, b0 = 48;
Action<double> write = d =>
{
if (d == 0)
bw.Write(b0);
else
{
if (d < 0)
{
bw.Write(minus);
d = -d;
}
double m = Math.Pow(10d, Math.Truncate(Math.Log10(d)));
while(true)
{
var r = ((decimal)(d / m) % 10); //decimal because of floating point errors
if (r == 0) break;
if (m == 0.1)
bw.Write(dec); //decimal point
bw.Write((byte)(48 + r));
m /= 10d;
}
}
bw.Write(semicol);
};
foreach (var d in test)
write(d);
}
}