1

我的代码是:

    int linenumber = File.ReadLines(path).Count();

但是大约 1 gig 大小的文件需要很长时间(大约 20 秒)。

那么有人知道解决这个问题的更好方法吗?

更新 6:

我已经测试了您的解决方案:

对于大约 870 mb 大小的文件:

方法一:{ my code time(seconds) : 13}

方法 2 : (from MarcinJuraszek & Locke) (同) {

time(seconds) : 12}

方法3:(来自Richard Deeming){ time(seconds) : 19}

方法4:(来自user2942249){ time(seconds) : 13}

方法 5 : (from Locke) {time(seconds) : 13是一样的lineBuffer = {4096 , 8192 , 16384 , 32768} }

方法 6 : (from Locke edition 2) { time(seconds) : 9 for Buffer size = 32KB, time(seconds) : 10 for Buffer size = 64KB }

正如我所说,在我的评论中,有一个应用程序(native code),可以在我的电脑中打开这个文件5 second。因此这是not about h.d.d speed.

By Compiling MSIL to Native Code,区别was not obvious

Conclusion: 这时候,Locke method 2就是faster比其他方法。

所以我把他的帖子标记为Answer。但是如果有人的话,这篇文章将被打开find better idea

vote up我为帮助我的亲爱的朋友+1 to solve the problem

谢谢你的帮助。有趣的更好的主意。最好的问候聪明人

4

4 回答 4

3

以下是一些可以快速完成的方法:

流阅读器:

using (var sr = new StreamReader(path))
{
    while (!String.IsNullOrEmpty(sr.ReadLine()))
        lineCount ++;
}

文件流:

var lineBuffer = new byte[65536]; // 64Kb
using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read,
       FileShare.Read, lineBuffer.Length))
{
    int readBuffer = 0;
    while ((readBuffer = fs.Read(lineBuffer, 0, lineBuffer.Length)) > 0)
    {
        for (int i = 0; i < readBuffer; i++)
        {
            if (lineBuffer[i] == 0xD) // Carriage return + line feed
                lineCount++;
        }
    }
}

多线程:

可以说线程数不应该影响读取速度,但现实世界的基准测试有时可以证明并非如此。尝试不同的缓冲区大小,看看你的设置是否有任何收获。 *此方法包含竞争条件。谨慎使用。

var tasks = new Task[Environment.ProcessorCount]; // 1 per core
var fileLock = new ReaderWriterLockSlim();
int bufferSize = 65536; // 64Kb

using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read,
        FileShare.Read, bufferSize, FileOptions.RandomAccess))
{
    for (int i = 0; i < tasks.Length; i++)
    {
        tasks[i] = Task.Factory.StartNew(() =>
            {
                int readBuffer = 0;
                var lineBuffer = new byte[bufferSize];

                while ((fileLock.TryEnterReadLock(10) && 
                       (readBuffer = fs.Read(lineBuffer, 0, lineBuffer.Length)) > 0))
                {
                    fileLock.ExitReadLock();
                    for (int n = 0; n < readBuffer; n++)
                        if (lineBuffer[n] == 0xD)
                            Interlocked.Increment(ref lineCount);
                }
            });
    }
    Task.WaitAll(tasks);
}
于 2013-11-05T20:12:34.303 回答
1

假设构建一个字符串来表示每一行是需要时间的,这样的事情可能会有所帮助:

public static int CountLines1(string path)
{
   int lineCount = 0;
   bool skipNextLineBreak = false;
   bool startedLine = false;
   var buffer = new char[16384];
   int readChars;

   using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, buffer.Length))
   using (var reader = new StreamReader(stream, Encoding.UTF8, false, buffer.Length, false))
   {
      while ((readChars = reader.Read(buffer, 0, buffer.Length)) > 0)
      {
         for (int i = 0; i < readChars; i++)
         {
            switch (buffer[i])
            {
               case '\n':
               {
                  if (skipNextLineBreak)
                  {
                     skipNextLineBreak = false;
                  }
                  else
                  {
                     lineCount++;
                     startedLine = false;
                  }
                  break;
               }
               case '\r':
               {
                  lineCount++;
                  skipNextLineBreak = true;
                  startedLine = false;
                  break;
               }
               default:
               {
                  skipNextLineBreak = false;
                  startedLine = true;
                  break;
               }
            }
         }
      }
   }

   return startedLine ? lineCount + 1 : lineCount;
}

编辑2:
他们所说的“假设”是真的!调用.Read()每个字符的开销超过了不为每一行创建字符串的节省。即使更新代码以一次读取一个字符块仍然比原始方法慢。

于 2013-11-05T18:09:31.980 回答
1

它取决于硬件,一个问题是最佳缓冲区大小是多少。也许等于或大于磁盘扇区大小。在进行了自己的实验之后,我发现通常最好让系统来确定。如果速度确实是一个问题,您可以下拉到 Win32 API ReadFile/CreateFile 指定各种标志和参数,例如异步 IO 和无缓冲、顺序读取等......这可能有助于提高性能,也可能不会。您必须进行概要分析并查看在您的系统上最有效的方法。在 .NET 中,您可以固定缓冲区以获得更好的性能,当然,在 GC 环境中固定内存还有其他后果,但如果您不将其保留太久,等等...

    const int bufsize = 4096;
    int lineCount = 0;
    Byte[] buffer = new Byte[bufsize];
    using (System.IO.FileStream fs = new System.IO.FileStream(@"C:\\data\\log\\20111018.txt", FileMode.Open, FileAccess.Read, FileShare.None, bufsize))
    {
        int totalBytesRead = 0;
        int bytesRead;
        while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0) {
            int i = 0;
            while (i < bytesRead)
            {
                switch (buffer[i])
                {
                    case 10:
                        {
                            lineCount++;
                            i++;
                            break;
                        }
                    case 13:
                        {
                            int index = i + 1;
                            if (index < bytesRead)
                            {
                                if (buffer[index] == 10)
                                {
                                    lineCount++;
                                    i += 2;
                                }
                            }
                            else
                            {
                                i++;
                            }
                            break;
                        }
                    default:
                        {
                            i++;
                            break;
                        }
                }
            }
            totalBytesRead += bytesRead;
        }
        if ((totalBytesRead > 0) && (lineCount == 0))
            lineCount++;                    
    }
于 2013-11-05T19:13:31.223 回答
1

正如您的测试所示,代码的更改不会对速度产生重大影响。瓶颈在于您的磁盘读取数据,而不是处理它的 C# 代码。

如果您想加快执行此任务的速度,请购买更快/更好的硬盘驱动器,要么具有更高的 RPM,要么甚至是固态驱动器。或者,您可以考虑使用 RAID0,这可能会提高您的磁盘读取速度。

另一种选择是拥有多个硬盘驱动器,并分解文件以便每个驱动器存储一个部分,然后您可以将工作与处理每个驱动器上的文件的任务并行化。(请注意,当您只有一个磁盘时并行化工作不会有任何帮助,而且实际上更有可能受到伤害。)

于 2013-11-05T20:16:50.030 回答