C#如何提高程序的效率?

3
我正在开发一个C#程序,用于读取非常大的文件并检查它们的不同属性和字段。我之前一直在使用不到100万行的文件进行测试,并且表现符合预期。但最近我对一个有250万行的文件进行了测试,需要花费4个小时才能运行完毕。
我使用自定义的读取函数逐个字符读取文件,以便找到所有CR和LF,因为每行都包含它们非常重要。我已经单独测试过读取函数,它花费了大约14分钟来读取整个文件,我认为这足够合理,可以读取1500个字符的250万行。我会把我的读取函数放在下面,但似乎这不是问题的原因。
我的读取函数将每个字符添加到一个字符串中,然后我检查字符串中的不同值。例如,行长度是否正确,文件是否包含标题以及标题是否包含正确的值。还有特定的值,如字符位置403-404是否为数字,字段1250-1300是否不为空等。
我的问题是,我该怎么做才能找出导致程序运行缓慢的原因并提高程序的效率?我尝试在每个行循环的开头和结尾检查时间,但似乎没有改变。但是,每10万行处理的时间都比前面的时间长得多。例如,处理第10,000到20,000行只需要不到3秒,而处理第830,000到840,000行需要约35秒。我考虑尝试使用多个线程,但认为在我从文件中读取行的情况下不会有帮助。你有什么想法吗?谢谢帮忙!
    static void ReadMyLine(ref string currentLine, string filePath, ref int asciiValue, ref Boolean isMissingCR, ref Boolean isMissingLF, ref Boolean isReversed, ref StreamReader file)
    {
        Boolean endOfRow = false;
        isMissingCR = false;
        isMissingLF = false;
        isReversed = false;

        currentLine = "";

        while (endOfRow == false)
        {
            asciiValue = file.Read();

            if (asciiValue == 10 || asciiValue == 13)
            {
                int asciiValueTemp = file.Peek();

                if (asciiValue == 13 && asciiValueTemp == 10)
                {
                    endOfRow = true;
                    asciiValue = file.Read();
                }
                else if (asciiValue == 10 && asciiValueTemp == 13)   // CRLF Reversed
                {
                    asciiValue = file.Read();
                    endOfRow = true;
                    isReversed = true;
                }
                else if (asciiValue == 10)                           // Missing CR
                {
                    isMissingCR = true;
                    endOfRow = true;
                }
                else if (asciiValue == 13)                           // Missing LF
                {
                    isMissingLF = true;
                    endOfRow = true;
                }
                else
                    endOfRow = true;
            }
            else if (asciiValue != -1)
                currentLine += char.ConvertFromUtf32(asciiValue);
            else
                endOfRow = true;
        }
    }

你有没有任何软件推荐? - buzzzzjay
Visual Studio已经内置了这个功能(至少在专业版中)。 - Alessandro Teruzzi
1个回答

11

这是我寻找的第一件事情,也是我要更改的第一件事情:

currentLine += char.ConvertFromUtf32(asciiValue);

不要那样做。在循环中使用字符串连接会拖慢性能 - 你将得到O(N2)的时间复杂度。应该使用StringBuilder代替。更多解释请参见我的文章,讲述何时使用StringBuilder

可能还有其他可做的优化,但只是改用StringBuilder就会有巨大的改善:

StringBuilder builder = new StringBuilder();
while (...)
{
    ...
    builder.Append(char.ConvertFromUtf32(asciiValue));
}
currentLine = builder.ToString();

你有很多引用参数,而且不清楚为什么要传递asciiValue。为什么要通过引用传递 StreamReader?如果一个方法使用这么多的引用参数,我会感到非常紧张 - 为什么你没有一个类型来封装你真正想从方法中返回的一切呢?

你可能需要阅读我的有关参数传递的文章,以更好地理解ref


1
我同意Jon的观点。每次你连接字符串时,实际上都会在内存中重新创建并引用前面的字符串副本 - 如果有很多这样的操作,肯定会对应用程序的性能产生影响。 - dooburt
2
@buzzzzjay:你从未改变过file的值,那么为什么要使用ref?如果你只想要asciiValue的值输出,你应该使用一个out参数 - 或者一个返回值!拥有一个带有ref参数的void方法真的很丑陋。但看起来这个方法确实想要返回多个信息:因此声明一个类型来封装这些信息,并将其作为该方法的返回类型。 - Jon Skeet
1
@buzzzzjay:这意味着您不理解在C#中处理StreamReader等引用类型时参数传递的工作原理。请阅读第二个链接... - Jon Skeet
@Joh Skeet:我已经阅读了它,不知道还有什么其他方法可以处理StreamReader。有什么建议吗? - buzzzzjay
1
@buzzzzjay:是的-不要通过引用传递它。你只传递了引用,而不是对象本身。由于从中读取而对对象进行的更改仍将对调用者可见。我建议你再次仔细阅读文章,特别是一个StringBuilder引用被按值传递的示例,然后向其附加“ world”。 - Jon Skeet
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接