我有生成数字并将其存储到List<int>
的功能
现在我必须尽快将这些结果存储到文件中。
到目前为止,这是我的代码:
private void Save_Click(object sender, EventArgs e)
{
//this is just for tests
List<int> myResults = Enumerable.Range(1, 50000000).ToList();
const string dir = @"D:\TESTS";
int fileCount = 1;
var file = Path.Combine(dir, string.Format("{0}.csv", fileCount));
var sw = new StreamWriter(file, false);
int i = 0;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
foreach (int res in myResults.Shuffle())
{
sw.WriteLine(res);
i++;
if (i%200000 != 0) continue;
fileCount++;
sw.Close();
file = Path.Combine(dir, string.Format("{0}.csv", fileCount));
sw = new StreamWriter(file, false);
}
sw.Close();
stopwatch.Stop();
label3.Text = string.Format("Save time(s): {0:0.##}", stopwatch.Elapsed.TotalSeconds);
}
随机播放是从this answer获取的扩展方法。
public static class Extensions
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng = null)
{
if (rng == null)
rng = new Random();
T[] elements = source.ToArray();
for (int i = elements.Length - 1; i > 0; i--)
{
int swapIndex = rng.Next(i + 1);
yield return elements[swapIndex];
elements[swapIndex] = elements[i];
}
yield return elements[0];
}
}
我的问题是我的电脑上保存大约需要5-7分钟,当我将结果数量增加到1亿时,我得到OutOfMemoryException
。
如何加快速度并消除错误?
答案 0 :(得分:4)
代码中最有问题的行是:
List<int> myResults = Enumerable.Range(1, 50000000).ToList();
和
foreach (int res in myResults.Shuffle())
尽量避免在堆上创建100m对象。相反,连续生成数据并立即将其写入磁盘而不将其保留在内存中。否则内存管理和垃圾收集成为瓶颈。
然后在定时代码之外移动洗牌。我很确定洗牌会吃掉相当多的时间。
因此,目前您测量的是.NET垃圾收集和混洗算法的效率,而不是您真正想要测量的效率,即编写CSV文件所需的时间。
答案 1 :(得分:1)
我在我的笔记本上运行了这个代码,没有shuffle方法,花了22秒。 所以我认为大部分时间可能都是采用这种方法。
我建议您在使用之前也不要创建数据,因为这会占用大量内存。创建一个枚举方法并按行返回数据。
您还在进行大量非常小的IO操作。 而是更少的更大的写入,所以尝试批量写入磁盘。 使用StringBuilder或类似的东西来创建更大的数据块来编写。 您还可以查看BufferedWriter类。