我有一个包含超过1百万行数据的CVS文件。我打算并行阅读它们以提高效率。我可以做类似以下的事情,还是有更有效的方法?
namespace ParallelData
{
public partial class ParallelData : Form
{
public ParallelData()
{
InitializeComponent();
}
private static readonly char[] Separators = { ',', ' ' };
private static void ProcessFile()
{
var lines = File.ReadLines("BigData.csv");
var numbers = ProcessRawNumbers(lines);
var rowTotal = new List<double>();
var totalElements = 0;
foreach (var values in numbers)
{
var sumOfRow = values.Sum();
rowTotal.Add(sumOfRow);
totalElements += values.Count;
}
MessageBox.Show(totalElements.ToString());
}
private static List<List<double>> ProcessRawNumbers(IEnumerable<string> lines)
{
var numbers = new List<List<double>>();
/*System.Threading.Tasks.*/
Parallel.ForEach(lines, line =>
{
lock (numbers)
{
numbers.Add(ProcessLine(line));
}
});
return numbers;
}
private static List<double> ProcessLine(string line)
{
var list = new List<double>();
foreach (var s in line.Split(Separators, StringSplitOptions.RemoveEmptyEntries))
{
double i;
if (Double.TryParse(s, out i))
{
list.Add(i);
}
}
return list;
}
private void button2_Click(object sender, EventArgs e)
{
ProcessFile();
}
}
}
答案 0 :(得分:10)
我不确定这是个好主意。根据您的硬件,CPU不会成为瓶颈,磁盘读取速度也会如此。
另一点:如果您的存储硬件是磁性硬盘,那么磁盘读取速度与文件在磁盘中的物理存储方式密切相关;如果文件没有碎片(即所有文件块都按顺序存储在磁盘上),如果你按顺序逐行读取,你的性能会更好。
一种解决方案是一次读取整个文件(如果你有足够的内存空间,100万行应该没问题)使用File.ReadAllLines
,将所有行存储在字符串数组中,然后处理(如果行顺序不重要,则在string.Split
中使用Parallel.Foreach
...等进行解析。
答案 1 :(得分:0)
通常,您应该尽量避免在多个线程上进行磁盘访问。磁盘是一个瓶颈,会阻塞,因此可能会影响性能。
如果文件中的行大小不是问题,您应该首先读取整个文件,然后并行处理。
如果文件太大而无法实现,那么您可以使用BlockingCollection加载它。使用一个线程读取文件并填充BlockingCollection,然后使用Parallel.ForEach处理其中的项目。 BlockingCollection允许您specify the max size of the collection,因此它只会读取文件中的更多行,因为处理和删除了集合中已有的行。
static void Main(string[] args)
{
string filename = @"c:\vs\temp\test.txt";
int maxEntries = 2;
var c = new BlockingCollection<String>(maxEntries);
var taskAdding = Task.Factory.StartNew(delegate
{
var lines = File.ReadLines(filename);
foreach (var line in lines)
{
c.Add(line); // when there are maxEntries items
// in the collection, this line
// and thread will block until
// the processing thread removes
// an item
}
c.CompleteAdding(); // this tells the collection there's
// nothing more to be added, so the
// enumerator in the other thread can
// end
});
while (c.Count < 1)
{
// this is here simply to give the adding thread time to
// spin up in this much simplified sample
}
Parallel.ForEach(c.GetConsumingEnumerable(), i =>
{
// NOTE: GetConsumingEnumerable() removes items from the
// collection as it enumerates over it, this frees up
// the space in the collection for the other thread
// to write more lines from the file
Console.WriteLine(i);
});
Console.ReadLine();
}
与其他一些人一样,我不得不提出这样一个问题:这是否真的需要尝试通过并行化进行优化,或者单线程解决方案是否能够运行良好?多线程增加了很多复杂性,有时候不值得。
您希望改进哪种表现?
答案 2 :(得分:0)
我检查了计算机上的这些行,看起来像使用Parallel来读取csv文件而没有任何cpu昂贵的计算没有意义。并行运行它比在一个线程中花费更多时间。这是我的结果: 对于上面的代码:
2699ms 2712ms (检查两次以确认结果)
然后用:
private static IEnumerable<List<double>> ProcessRawNumbers2(IEnumerable<string> lines)
{
var numbers = new List<List<double>>();
foreach(var line in lines)
{
lock (numbers)
{
numbers.Add(ProcessLine(line));
}
}
return numbers;
}
给我: 2075ms 2106ms
所以我认为如果csv中的那些数字不需要在程序中以某种方式(通过一些大量的计算等)计算然后存储在程序中,那么在这种情况下使用并行性是没有意义的,这样就添加一些开销。