为什么这个Parallel.ForEach循环不能提高性能?

5
我有以下代码:
           if (!this.writeDataStore.Exists(mat))
            {
                BlockingCollection<ImageFile> imageFiles = new BlockingCollection<ImageFile>();
                Parallel.ForEach(fileGrouping, fi => DecompressAndReadGzFile(fi, imageFiles));


                this.PushIntoDb(mat, imageFiles.ToList());
            }

DecompressAndReadGzFile 是同一类中包含该方法的静态方法。根据方法名称,我正在解压缩和读取gz文件,很多文件,即多达1000个,因此并行化的开销是值得的。然而,我没有看到好处。当我使用ANTS性能分析器时,我发现它们运行的时间与未进行并行化时完全相同。我还使用进程资源管理器检查CPU核心,看起来可能在两个核心上处理工作,但一个核心似乎在大部分工作。我在使用Parallel.ForEach进行文件解压缩和读取时有何误解?

更新的问题:从文件列表中读取信息的最快方法是什么?

问题(简化):

  1. 有一个大的.gz文件列表(1200)。
  2. 每个文件都有一个包含“DATA:”的行,位置和行号不是静态的,可以从文件到文件不同。
  3. 我们需要检索“DATA:”后面的第一个数字(仅为简单起见),并将其存储在内存中的对象中(例如List)。

在最初的问题中,我使用了Parallel.ForEach循环,但似乎只有一个核心被占用。


DecompressAndReadGzFile 函数中是否进行了同步操作? - SimonC
据我所知,没有这样的情况发生。虽然有一个对imageFiles.Add的调用会自动添加一个锁,就我所了解的情况而言。 - Seth
2个回答

12

线程是否大部分时间都在等待IO呢?如果一次读取多个文件,可能会使磁盘比单个操作更加劳累。如果您使用一个线程按顺序读取,然后将CPU密集型的解压分配给单独的线程,那么可能可以提高性能......但是,如果磁盘比解压缩过程本身更慢,则实际上你可能只需要一个线程执行解压缩。

测试这一点的一种方法是首先将需要解压缩的文件复制到Ramdisk上,然后仍然使用当前代码。 我怀疑您会发现自己处于CPU密集型状态,并且所有处理器几乎一直很忙。

(您还应该考虑对已解压缩的文件所做的事情。您是否将其写回磁盘?如果是这样,再次有可能你要等待磁盘打满。)


我没有将解压缩的文件写入磁盘。我正在使用GZipStream将压缩文件读入内存,创建一个TextReader来提取我需要的内容,并将其放入上面代码中的imageFiles集合中。不过,我可能会尝试您的RAM磁盘建议。 - Seth
@Seth:请注意,ramdisk建议仅仅是为了验证您是否受到IO限制而不是CPU限制。如果是这种情况,那么您最终只会将成本转移到“将数据复制到ramdisk”阶段。 - Jon Skeet
还记得那些年前的硬盘加速软件驱动程序吗?它们通过在内存中压缩数据,然后将压缩数据写入磁盘来加快磁盘I/O速度。 - dthorpe

0

有没有可能您的静态方法在其调用之间共享任何全局资源。 因为在这种情况下,该静态方法将被顺序调用,没有并行优势。 您能提供fileGrouping类代码吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接