CsvHelper 并并行查询大型 csv 文件
CsvHelper and querying a large csv file in parallel
我有一个 3.6 gig 的 csv 文件。我正在使用 CsvHelper 来处理它。当我使用 linq 查询它时,它需要几分钟,我看到 CPU 在我的 PC 上最多只能显示大约 25%。执行此操作时,Linq 似乎可以很好地处理内存,因为它根本不会增加太多。
所以我想通过添加 .AsParallel() 我应该会看到一些性能提升。当我 运行 它时,我看到我的 CPU 上升到大约 95%,但它需要同样长的时间。
为什么我看不到 .AsParallel() 的性能提升,有什么方法可以通过它获得更好的性能(将其保留为 csv)。
string path = @"C:\my_3_gig_file.csv";
using (var csv = new CsvHelper.CsvReader(new StreamReader(path, Encoding.Default)))
{
csv.Configuration.Delimiter = ",";
csv.Configuration.TrimFields = true;
var records = csv.GetRecords<MyClass>();
var q1 = (from a in records.AsParallel()
where a.MY_HOUR == "1"
&& a.MY_DATE == "14-JUN-13"
select a).ToList();
}
尝试并行处理行不会有任何收获,因此尝试没有意义。您只能使用 CsvHelper 线性读取和处理文件。即便如此,也没有足够的工作来证明行的并行处理是合理的。可能伤害你的是建立每一个被读取的记录。如果有很多列,那么每行的处理量就会很大。
您正在尝试从文件中过滤出要使用的行。您应该直接读入行,检查列,然后边做边建立记录。这样,您就不会浪费大量时间为每一行构建记录,而只是可能将其丢弃。
这是您可以执行此操作的一种方法。
List<T> GetCsvRecordsFiltered<T>(string path, Func<CsvReader, bool> filter, Action<CsvConfiguration> configure = null) where T : class
{
using (var file = File.OpenText(path))
using (var reader = new CsvReader(file))
{
configure?.Invoke(reader.Configuration);
var result = new List<T>();
while (reader.Read())
if (filter(reader))
result.Add(reader.GetRecord<T>());
return result;
}
}
然后当你读取文件时,你会这样做:
var q1 = GetCsvRecordsFiltered<MyClass>(path,
reader => reader["MY_HOUR"] == "1" && reader["MY_DATE"] == "14-JUN-13",
config => config.TrimFields = true
);
使用 AsParallel()
你不会看到任何性能提升,因为没有什么可以并行完成的。
之所以csv.GetRecords<MyClass>()
returns是因为它yields记录。除非需要,否则它实际上不会读取任何文件。读取的每条记录只会从文件中读取一条记录(实际上是数据缓冲区)。
当您使用 linq 时,它必须读取整个 3.6 GB 的文件并对其进行解析以获得结果。您不可能执行 where
子句而不让它读取整个文件。当有其他 linq 提供程序时不会发生的原因,比如 sql,是因为建立了索引,并且 sql 只能提取它需要的记录。
我有一个 3.6 gig 的 csv 文件。我正在使用 CsvHelper 来处理它。当我使用 linq 查询它时,它需要几分钟,我看到 CPU 在我的 PC 上最多只能显示大约 25%。执行此操作时,Linq 似乎可以很好地处理内存,因为它根本不会增加太多。
所以我想通过添加 .AsParallel() 我应该会看到一些性能提升。当我 运行 它时,我看到我的 CPU 上升到大约 95%,但它需要同样长的时间。
为什么我看不到 .AsParallel() 的性能提升,有什么方法可以通过它获得更好的性能(将其保留为 csv)。
string path = @"C:\my_3_gig_file.csv";
using (var csv = new CsvHelper.CsvReader(new StreamReader(path, Encoding.Default)))
{
csv.Configuration.Delimiter = ",";
csv.Configuration.TrimFields = true;
var records = csv.GetRecords<MyClass>();
var q1 = (from a in records.AsParallel()
where a.MY_HOUR == "1"
&& a.MY_DATE == "14-JUN-13"
select a).ToList();
}
尝试并行处理行不会有任何收获,因此尝试没有意义。您只能使用 CsvHelper 线性读取和处理文件。即便如此,也没有足够的工作来证明行的并行处理是合理的。可能伤害你的是建立每一个被读取的记录。如果有很多列,那么每行的处理量就会很大。
您正在尝试从文件中过滤出要使用的行。您应该直接读入行,检查列,然后边做边建立记录。这样,您就不会浪费大量时间为每一行构建记录,而只是可能将其丢弃。
这是您可以执行此操作的一种方法。
List<T> GetCsvRecordsFiltered<T>(string path, Func<CsvReader, bool> filter, Action<CsvConfiguration> configure = null) where T : class
{
using (var file = File.OpenText(path))
using (var reader = new CsvReader(file))
{
configure?.Invoke(reader.Configuration);
var result = new List<T>();
while (reader.Read())
if (filter(reader))
result.Add(reader.GetRecord<T>());
return result;
}
}
然后当你读取文件时,你会这样做:
var q1 = GetCsvRecordsFiltered<MyClass>(path,
reader => reader["MY_HOUR"] == "1" && reader["MY_DATE"] == "14-JUN-13",
config => config.TrimFields = true
);
使用 AsParallel()
你不会看到任何性能提升,因为没有什么可以并行完成的。
之所以csv.GetRecords<MyClass>()
returns是因为它yields记录。除非需要,否则它实际上不会读取任何文件。读取的每条记录只会从文件中读取一条记录(实际上是数据缓冲区)。
当您使用 linq 时,它必须读取整个 3.6 GB 的文件并对其进行解析以获得结果。您不可能执行 where
子句而不让它读取整个文件。当有其他 linq 提供程序时不会发生的原因,比如 sql,是因为建立了索引,并且 sql 只能提取它需要的记录。