在 C# 中快速读取 100+ GB 文件行的最快方法
Fastest way to read rows form 100+ GB files fast in C#
正在处理一个将加载 100+ GB 文本文件的项目,其中一个过程是计算指定文件中的行数。我必须按照以下方式进行操作才能避免内存不足异常。有没有更快的方法或者什么是多任务最有效的方法。 (我知道你可以在 4 个线程上做类似 运行 的事情,并将组合输出除以 4。不知道最有效的方法)
uint loadCount2 = 0;
foreach (var line in File.ReadLines(currentPath))
{
loadCount2++;
}
计划在 运行 将程序安装在具有 4 个双核 CPU 和 40 GB RAM 的服务器上,当我固定好它的位置后。目前,它 运行s 在一个临时的小型 4 核 8GB RAM 服务器上。 (不知道线程在多个 CPU 上的表现如何。)
我测试了很多你的建议。
Stopwatch sw2 = Stopwatch.StartNew();
{
using (FileStream fs = File.Open(json, FileMode.Open))
CountLinesMaybe(fs);
}
TimeSpan t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
string answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
sw2.Restart();
loadCount2 = 0;
Parallel.ForEach(File.ReadLines(json), (line) =>
{
loadCount2++;
});
t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
sw2.Restart();
loadCount2 = 0;
foreach (var line in File.ReadLines(json))
{
loadCount2++;
}
t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
sw2.Restart();
loadCount2 = 0;
int query = (int)Convert.ToByte('\n');
using (var stream = File.OpenRead(json))
{
int current;
do
{
current = stream.ReadByte();
if (current == query)
{
loadCount2++;
continue;
}
} while (current != -1);
}
t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
Console.ReadKey();
private const char CR = '\r';
private const char LF = '\n';
private const char NULL = (char)0;
public static long CountLinesMaybe(Stream stream)
{
//Ensure.NotNull(stream, nameof(stream));
var lineCount = 0L;
var byteBuffer = new byte[1024 * 1024];
const int BytesAtTheTime = 4;
var detectedEOL = NULL;
var currentChar = NULL;
int bytesRead;
while ((bytesRead = stream.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
{
var i = 0;
for (; i <= bytesRead - BytesAtTheTime; i += BytesAtTheTime)
{
currentChar = (char)byteBuffer[i];
if (detectedEOL != NULL)
{
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 1];
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 2];
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 3];
if (currentChar == detectedEOL) { lineCount++; }
}
else
{
if (currentChar == LF || currentChar == CR)
{
detectedEOL = currentChar;
lineCount++;
}
i -= BytesAtTheTime - 1;
}
}
for (; i < bytesRead; i++)
{
currentChar = (char)byteBuffer[i];
if (detectedEOL != NULL)
{
if (currentChar == detectedEOL) { lineCount++; }
}
else
{
if (currentChar == LF || currentChar == CR)
{
detectedEOL = currentChar;
lineCount++;
}
}
}
}
if (currentChar != LF && currentChar != CR && currentChar != NULL)
{
lineCount++;
}
return lineCount;
}
结果显示进步很大,但我希望能达到 20 分钟。
我想让他们在我更强大的服务器上看到拥有更多 CPU 的效果。
第二个 运行 返回:
23分钟,
25分钟,
22分钟,
29 分钟
意味着这些方法并没有真正产生任何区别。
(无法截屏,因为我删除了暂停,程序通过清屏继续运行)
对于接近 GB
的文件,基于 ReadByte(并与换行符进行比较)的方法可能比 ReadLine.For 示例更快
stopwatch = System.Diagnostics.Stopwatch.StartNew();
uint count = 0;
int query = (int)Convert.ToByte('\n');
using (var stream = File.OpenRead(filepath))
{
int current;
do
{
current = stream.ReadByte();
if (current == query)
{
count++;
continue;
}
} while (current!= -1);
}
Console.WriteLine($"Using ReadByte,Time : {stopwatch.Elapsed.TotalMilliseconds},Count: {r}");
使用 ReadByte,时间:8174.5661,计数:7555107
stopwatch = System.Diagnostics.Stopwatch.StartNew();
uint loadCount2 = 0;
foreach (var line in File.ReadLines(filepath))
{
loadCount2++;
}
Console.WriteLine($"Using ReadLines, Time : {stopwatch.Elapsed.TotalMilliseconds},Count: {r}");
使用 ReadLines,时间:27303.835,计数:7555107
当您开始使用大数据时,您需要一个更强大的计算系统来使事情 运行 更快。如果您想要速度,请增加 RAM 以将整个数据保存在内存中。添加 NVMe SSD 并将数据文件存储在其上以获得更快的读取性能。
软件方面,只需大块读取文件并遍历缓冲区检查每个字节计数换行符。您没有对文本行添加或删除字符、检查模式等进行任何处理。ReadLine 在创建其数据结构时有太多开销,无法动态保存这些行。
您不需要该开销,而只需分配一次大的固定大小的缓冲区、读入数据并迭代查找换行符。也用 C 语言编写,以加快处理速度。
正在处理一个将加载 100+ GB 文本文件的项目,其中一个过程是计算指定文件中的行数。我必须按照以下方式进行操作才能避免内存不足异常。有没有更快的方法或者什么是多任务最有效的方法。 (我知道你可以在 4 个线程上做类似 运行 的事情,并将组合输出除以 4。不知道最有效的方法)
uint loadCount2 = 0;
foreach (var line in File.ReadLines(currentPath))
{
loadCount2++;
}
计划在 运行 将程序安装在具有 4 个双核 CPU 和 40 GB RAM 的服务器上,当我固定好它的位置后。目前,它 运行s 在一个临时的小型 4 核 8GB RAM 服务器上。 (不知道线程在多个 CPU 上的表现如何。)
我测试了很多你的建议。
Stopwatch sw2 = Stopwatch.StartNew();
{
using (FileStream fs = File.Open(json, FileMode.Open))
CountLinesMaybe(fs);
}
TimeSpan t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
string answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
sw2.Restart();
loadCount2 = 0;
Parallel.ForEach(File.ReadLines(json), (line) =>
{
loadCount2++;
});
t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
sw2.Restart();
loadCount2 = 0;
foreach (var line in File.ReadLines(json))
{
loadCount2++;
}
t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
sw2.Restart();
loadCount2 = 0;
int query = (int)Convert.ToByte('\n');
using (var stream = File.OpenRead(json))
{
int current;
do
{
current = stream.ReadByte();
if (current == query)
{
loadCount2++;
continue;
}
} while (current != -1);
}
t = TimeSpan.FromMilliseconds(sw2.ElapsedMilliseconds);
answer = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms", t.Hours, t.Minutes, t.Seconds, t.Milliseconds);
Console.WriteLine(answer);
Console.ReadKey();
private const char CR = '\r';
private const char LF = '\n';
private const char NULL = (char)0;
public static long CountLinesMaybe(Stream stream)
{
//Ensure.NotNull(stream, nameof(stream));
var lineCount = 0L;
var byteBuffer = new byte[1024 * 1024];
const int BytesAtTheTime = 4;
var detectedEOL = NULL;
var currentChar = NULL;
int bytesRead;
while ((bytesRead = stream.Read(byteBuffer, 0, byteBuffer.Length)) > 0)
{
var i = 0;
for (; i <= bytesRead - BytesAtTheTime; i += BytesAtTheTime)
{
currentChar = (char)byteBuffer[i];
if (detectedEOL != NULL)
{
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 1];
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 2];
if (currentChar == detectedEOL) { lineCount++; }
currentChar = (char)byteBuffer[i + 3];
if (currentChar == detectedEOL) { lineCount++; }
}
else
{
if (currentChar == LF || currentChar == CR)
{
detectedEOL = currentChar;
lineCount++;
}
i -= BytesAtTheTime - 1;
}
}
for (; i < bytesRead; i++)
{
currentChar = (char)byteBuffer[i];
if (detectedEOL != NULL)
{
if (currentChar == detectedEOL) { lineCount++; }
}
else
{
if (currentChar == LF || currentChar == CR)
{
detectedEOL = currentChar;
lineCount++;
}
}
}
}
if (currentChar != LF && currentChar != CR && currentChar != NULL)
{
lineCount++;
}
return lineCount;
}
结果显示进步很大,但我希望能达到 20 分钟。 我想让他们在我更强大的服务器上看到拥有更多 CPU 的效果。
第二个 运行 返回: 23分钟, 25分钟, 22分钟, 29 分钟
意味着这些方法并没有真正产生任何区别。 (无法截屏,因为我删除了暂停,程序通过清屏继续运行)
对于接近 GB
的文件,基于 ReadByte(并与换行符进行比较)的方法可能比 ReadLine.For 示例更快stopwatch = System.Diagnostics.Stopwatch.StartNew();
uint count = 0;
int query = (int)Convert.ToByte('\n');
using (var stream = File.OpenRead(filepath))
{
int current;
do
{
current = stream.ReadByte();
if (current == query)
{
count++;
continue;
}
} while (current!= -1);
}
Console.WriteLine($"Using ReadByte,Time : {stopwatch.Elapsed.TotalMilliseconds},Count: {r}");
使用 ReadByte,时间:8174.5661,计数:7555107
stopwatch = System.Diagnostics.Stopwatch.StartNew();
uint loadCount2 = 0;
foreach (var line in File.ReadLines(filepath))
{
loadCount2++;
}
Console.WriteLine($"Using ReadLines, Time : {stopwatch.Elapsed.TotalMilliseconds},Count: {r}");
使用 ReadLines,时间:27303.835,计数:7555107
当您开始使用大数据时,您需要一个更强大的计算系统来使事情 运行 更快。如果您想要速度,请增加 RAM 以将整个数据保存在内存中。添加 NVMe SSD 并将数据文件存储在其上以获得更快的读取性能。
软件方面,只需大块读取文件并遍历缓冲区检查每个字节计数换行符。您没有对文本行添加或删除字符、检查模式等进行任何处理。ReadLine 在创建其数据结构时有太多开销,无法动态保存这些行。
您不需要该开销,而只需分配一次大的固定大小的缓冲区、读入数据并迭代查找换行符。也用 C 语言编写,以加快处理速度。