【问题标题】:Optimization for splitting one file into many small files将一个文件拆分为多个小文件的优化
【发布时间】:2013-12-11 16:34:51
【问题描述】:

我正在尝试将一个大文件(逗号分隔,每个术语用双引号括起来)拆分为许多较小的文件,基于每个记录中第一项的键,通常有多个相同的记录键。

这个大文件可以从 1GB 到 2GB,生成的文件数量可以从 10,000-30,000 不等,每个文件在一个以密钥命名的子文件夹中。

在 C# 中,我在每一行上执行 StreamReader.ReadLine(),将结果连接起来,直到它到达不同的键(表示前一个键的最后一个数据),然后调用一个函数来异步写入文件.我正在调用 windows sort 对这些文件进行排序以使键连续(因此我只需打开文件一次),但操作完成仍需要大约 20 分钟。有什么办法可以加快这个速度吗?

sfd = new SaveFileDataDelegate(this.SaveFileData);



private void CSVParse(string filename, string unzippedFilePath, string feedname)
{
    StreamReader filestream = null;
    FileStream readerStream = null;
    try
    {
        readerStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None, 120000, FileOptions.SequentialScan);
        filestream = new StreamReader(readerStream, Encoding.UTF8, false, 120000);

        string tempstring = "";
        string buffer = "";
        string lastlotkey = "";
        IAsyncResult result = null;

        activityLog.Log("Parsing File: " + filename);

        while (((tempstring = filestream.ReadLine()) != null) || buffer != "")
        {
            if (tempstring == null)
            {
                tempstring = "";
            }
            string lotkey =  tempstring.Replace("\"","").Split(',').First();
            if (lotkey == tempstring && tempstring != "")
            {
                break;
            }
            if (lotkey == "DealerID")
            {
                continue;
            }
            if (lastlotkey == "")
            {
                lastlotkey = lotkey;
            }
            if ((lotkey != lastlotkey && buffer.Length > 0))
            {
                result = sfd.BeginInvoke(outputDirectory + @"\" + feedname + @"\" + lastlotkey + @"\" + (filename.Split('\\').Last()).Split('.').First() + ".txt", buffer, outputDirectory + @"\" + feedname + @"\" + lastlotkey,null,null);
                lastlotkey = lotkey;
                buffer = "";

                if (tempstring == "")
                {
                    continue;
                }
            }
            if (buffer.Length > 0)
            {
                buffer = buffer + "\r\n";
            }
            buffer = buffer + tempstring;
        }
        filestream.Close();
        readerStream.Close();
        if (result != null)
        {
            result.AsyncWaitHandle.WaitOne(-1);
        }
        return;
    }

    catch (Exception e)
    {
        activityLog.Log("Error Occurred:  " + e.ToString());
        if (filestream != null)
        {
            filestream.Close();
        }
        hadError = true;
        return;
    }
}


private void SaveFileData(string file, string buffer, string directory)
{
    // create file from last lot key with data from parsing, write, close, update lastlotkey
    Directory.CreateDirectory(directory);
    FileStream fs = null;
    StreamWriter temp = null;
    try
    {
        if (!File.Exists(file))
        {
            fs = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 120000);
        }
        else
        {
            fs = new FileStream(file, FileMode.Truncate, FileAccess.Write, FileShare.None, 120000);
        }
        temp = new StreamWriter(fs, Encoding.UTF8, 120000);
        temp.AutoFlush = false;
        temp.WriteLine(headerLine);
        temp.Write(buffer);
        temp.Flush();
        temp.Close();
        fs.Close();
    }
    catch (Exception e)
    {
        activityLog.Log("Error Occurred:  " + e.ToString());
        if (fs != null)
        {
            fs.Close();
        }
        if (temp != null)
        {
            temp.Close();
        }
        hadError = true;
        return;
    }
}

编辑

我爬过 Stack Overflow 和互联网的最深处,在逐行分析之后,我发现字符串连接实际上是解析例程的繁重工作(在文件复制和 Windows 排序之后),用 Stringbuilder 替换它取得了巨大的进步,总处理时间从 20 分钟(复制+排序+解析)下降到 5 分钟复制+排序和 2 分钟解析,总共 7 分钟。速度提升 130%

【问题讨论】:

  • StringBuilder 再次出击。我想知道尝试调试使用 String 不正确编写的程序所浪费的时间是否超过了 String interning 在世界上节省的总内存。

标签: c# file-io io text-parsing


【解决方案1】:

如果删除写入硬盘的代码,它的速度有多快? 很多减速将是因为硬盘驱动器。得到一个ssd:P

由于您循环很多,我会限制循环内的代码。删除重复代码,并在循环外获取尽可能多的代码。

第一个 if 不需要,因为您已经在 while 中检查 null。

如果您有很多类似的行,则可能不需要一直拆分。您可以改用 .StartsWith。

如果文件一致,就不需要去掉"。可以和"比较。

因为您在第二个 if 中检查空临时字符串。也许你想在拆分之前这样做,因为拆分空字符串是没有用的。

您为获取新文件名所做的许多字符串操作都可以在循环之外完成。

【讨论】:

  • 这似乎有点帮助,我的处理时间似乎提高了大约 5%,我仍然偷偷怀疑我的瓶颈与磁盘 IO 相关,您对打开/写入逻辑有什么建议吗文件、缓冲区大小等?
  • @WestonM 让IO更快的唯一方法就是限制读/写次数和限制针的移动(例如:读/写很多不同的文件会使针移动不仅仅是写入或读取一个文件)。或者升级到更快的硬件。
猜你喜欢
  • 1970-01-01
  • 2017-08-21
  • 1970-01-01
  • 1970-01-01
  • 2019-09-23
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
  • 2013-08-24
相关资源
最近更新 更多