我在Azure blob存储上有数十亿个xml日志文件,需要进行处理,查询和结果存储。我正在使用Parallel.Foreach,因为文件处理是相互独立的。
Parallel.ForEach<String> (listOfFeatureFiles, file => {
//For each file that was created
string fileName = file;
string directoryPath = outputfolderPath + "/" + FeatureFolderName;
string finalFilePath = directoryPath + "/" + fileName;
DownloadContent();
XMLParseAndQueryData();
UploadResultToQueue();
DeleteLocalCopy();
});
如果它只是计算密集型,那么我可能拥有最大的CPU使用率,但是在我的场景中,20%的文件比其余80%的文件大得多(以GB为单位)。这通常导致4个内核的CPU使用率仅为50%。如何优化它以实现最大CPU使用率,即&gt; 90%?
我的假设是,一旦任务下载大文件,就不会使用cpu,但同时也不会创建新线程,这可以利用处理能力。我对这个假设可能是错的,并且会理解与其否定的具体联系。
答案 0 :(得分:1)
我为我的一个客户构建了一个类似的应用程序,它也处理了大量不同大小的xml文件。下载会干扰CPU的使用,你无能为力。但是,您可以通过使用具有多个使用者的BlockingCollection来优化CPU使用率,并在下载较大文件时始终保持处理较小的文件。
答案 1 :(得分:1)
我的假设是,一旦任务下载大文件,就不会使用cpu,但同时也不会创建新的线程,这可以利用处理能力。
您确定您有足够的网络带宽吗?下载文件实际上并不是此过程的瓶颈吗?
如果你是,并且缓慢添加线程实际上是减慢你的速度,那么快速而肮脏的解决方案是强制ThreadPool
(内部由Parallel.ForEach()
使用)来有更多的线程。您可以致电ThreadPool.SetMinThreads
。
正确的解决方案是使IO绑定方法异步,并独立于CPU绑定方法安排它们。为了帮助安排,您可以使用TPL数据流(EnsureOrdered
需要预发布版本):
var cpuBoundOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = Environment.ProcessorCount,
EnsureOrdered = false
};
var ioBoundOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 10, // TODO: tweak this value as necessary
EnsureOrdered = false
};
var downloadBlock = new TransformBlock<string, string>(async file =>
{
await DownloadContentAsync(file);
return file;
}, ioBoundOptions);
var parseBlock = new TransformBlock<string, string>(file =>
{
XMLParseAndQueryData(file);
return file;
}, cpuBoundOptions);
var uploadBlock = new TransformBlock<string, string>(async file =>
{
await UploadResultToQueue(file);
return file;
}, ioBoundOptions);
var deleteBlock = new ActionBlock<string>(file => DeleteLocalCopy(file));
var linkOptions = new DataflowLinkOptions { PropagateCompletion = true };
downloadBlock.LinkTo(parseBlock, linkOptions);
parseBlock.LinkTo(uploadBlock, linkOptions);
uploadBlock.LinkTo(deleteBlock, linkOptions);
foreach (var file in listOfFeatureFiles)
{
downloadBlock.Post(file);
}
downloadBlock.Complete();
await deleteBlock.Completion;