具有有序输入和输出的并行管道

时间:2016-08-16 23:24:45

标签: c# multithreading algorithm pipeline tpl-dataflow

我在C#中完成了一项非常简单的任务。用户给我一个表示大型二进制文件(大小为几十GB)的流。该文件由许多不同的块组成。我需要阅读每个块;对每个块执行一些CPU密集型分析;然后以正确的顺序给用户结果。在伪代码中,此代码可能如下所示:

public IEnumerable<TResult> ReadFile(Stream inputStream) {
    while(true) {
        byte[] block = ReadNextBlock(stream);
        if (block == null) {
            break; // EOF
        }  
        TResult result = PerformCpuIntensiveAnalysis(block);
        yield return result;
    }
}

这可以正常工作,但速度很慢,因为它只使用一个CPU内核进行CPU密集型分析。我想要做的是逐个读取块,并行分析它们,然后以与文件中遇到的块相同的顺序将结果返回给用户。当然,我无法将整个文件读入内存,所以我想在任何给定时间限制我保留在队列中的块数。

有很多解决方案,我已经尝试了一对;但是,出于某种原因,我找不到任何明显优于天真方法的解决方案:

public IEnumerable<TResult> ReadFile(Stream inputStream) {
    while(true) {
        var batch = new List<byte[]>();
        for (int i=0; i<BATCH_SIZE; i++) {
            byte[] block = ReadNextBlock(stream);
            if (block == null) {
                break;
            }  
            batch.Add(block);
        }
        if (batch.Count == 0) {
            break;
        }
        foreach(var result in batch
            .AsParallel()
            .AsOrdered()
            .Select(block => PerformCpuIntensiveAnalysis(block))
            .ToList()) {
            yield return result; 
        }
    }
}

我尝试过TPL /数据流以及纯粹的手动方法,在每种情况下,我的代码都花费大部分时间等待同步。它的性能优于串行版本约2倍,但在具有8个内核的机器上,我预计会有更多。那么,我做错了什么?

(我还应该澄清一点,我并没有真正使用&#34; yield return&#34;我的代码中的生成器模式,我只是为了简洁而在这里使用它。)

1 个答案:

答案 0 :(得分:1)

尝试优化块大小。

如果块太少而且其中一块比其他块需要更长的时间,那么只有一个CPU必须完成几乎所有的工作。

另一方面,如果块太小,TPL将花费大量时间来处理与任务管理相关的开销。

你应该拥有比CPU更多的块。这允许TPL将工作均匀地分配给CPU。另一方面,一个块应该需要大量的计算工作。所以很难给出具体的数字。