C#非常慢的StreamReader

时间:2012-03-31 12:37:57

标签: c# performance streamreader tab-delimited-text

我正在使用一些不是我编写的最佳代码......: - |

我有以下代码:

string fmtLine = "";
            string[] splitedFmtLine;
            int counterFMTlines = 0;

            foreach (string fmtF in fmtFiles)
            {
                using (StreamReader sr = new StreamReader(fmtF))
                {
                    while ((fmtLine = sr.ReadLine()) != null)
                    {
                        Console.WriteLine(counterFMTlines++);
                        foreach (L3Message message in rez)
                        {
                            splitedFmtLine = Regex.Split(fmtLine, "\t");

                            if (message.Time == splitedFmtLine[0])
                            {
                                message.ScramblingCode = splitedFmtLine[7];      
                            }
                        }
                    }
                }
            }

我在List为空时测试了这段代码,并且只有一个文件(制表符分隔,280000行),即使这样,也需要一生(1分钟)来浏览我文件的所有280000行。这意味着执行跳过了foreach循环,其中是myObjs列表。

我无法理解为什么花了这么长时间?

例如,我使用不同的文本文件(源文件)填充myObjs列表(树层次结构),但是大于此制表符分隔(制表符分隔:16MB,源文件:36MB)并且它仅花费了第二个分钟。

2 个答案:

答案 0 :(得分:7)

您正在向控制台写入280.000次,这非常慢。删除控制台输出。另外,使用string.Split('\ t'),这比这个特定的正则表达式调用快。

答案 1 :(得分:0)

除了写入控制台的问题之外,还有一个 O(m * n)运行时,其中n是文件中的行数,m是消息数。如果m或n很大,那就太糟糕了。您可以使用Dictionary代替并消除内循环,将其减少为O(n)操作。

您可以使用时间作为键将消息放入Dictionary。在循环中,您只需要在字典中询问特定时间的消息:

        string fmtLine = "";
        string[] splitedFmtLine;
        int counterFMTlines = 0;

        var messageTimes = new Dictionary<string, LinkedList<L3Message>>();
        foreach (L3Message message in rez)
        {
            LinkedList<L3Message> list=null;
            messageTimes.TryGetValue(message.Time, out list);

            list = list ?? new LinkedList<L3Message>();

            list.AddLast(message);
            messageTimes[message.Time] = list;
        }

        foreach (string fmtF in fmtFiles)
        {
            using (StreamReader sr = new StreamReader(fmtF))
            {
                while ((fmtLine = sr.ReadLine()) != null)
                {
                    //Console.WriteLine(counterFMTlines++);
                    splitedFmtLine = fmtLine.Split('\t');

                    LinkedList<L3Message> messageList = null;
                    messageTimes.TryGetValue(splitedFmtLine[0], out messageList);

                    if(messageList != null)
                    {
                        foreach (var message in messageList)
                        {
                            message.ScramblingCode = splitedFmtLine[7];                                
                        }
                        messageTimes.Remove(splitedFmtLine[0]); //see comments
                    }

                    if(messageTimes.Count==0) break; //see comments
                }
            }
            if(messageTimes.Count==0) break; //see comments
        } 

这应该超级快。

修改:我更改了示例,以便它支持一次有多条消息的情况。

Edit2:我根据消息时间和ScramblingCode始终关联的事实添加了优化(请参阅注释)。