为什么使用两个任务执行两个长计算会降低性能



我正在制作C#软件来实现RFC 1951"Deflate"压缩。当选择块边界以最大化压缩时,有机会并行计算两个替代块选择的大小,以提高性能(这是相当长的计算,涉及霍夫曼码的计算)。

以下是非并行版本:

int bits2 = b2.GetBits();
int bits3 = b3.GetBits();  

这是并行版本:

Task<int> t2 = Task<int>.Factory.StartNew( () => { return b2.GetBits(); } );
int bits3 = b3.GetBits(), bits2 = t2.Result;  

但是并行版本实际上运行得更慢,我不明白为什么。如果相关,处理器是英特尔酷睿 i7-6700HQ。完整的代码在这里: https://github.com/georgebarwood/pdf/blob/master/Deflator.cs

为什么并行版本运行得

更慢而不是更快,我是否犯了一个错误,我能做些什么来使并行版本比非并行版本运行得更快?

如果我在计算机上运行您的GetBits方法,它平均运行不到 3μs。并行运行代码会产生一些开销。事实上,在调用方,调用Task.Factory.StartNew也需要 2 到 3 毫秒(我没有测量任务实际开始执行前多长时间)。因此,在您的情况下,开销会抵消潜在的收益。

这是使算法高效并行运行的困难之一:您需要确保工作单元足够大以抵消诱导的开销。

为了回答我自己的问题"我能做些什么来使并行版本比非并行版本运行得更快?",我现在重新设计了代码以使用两个线程 - 第二个线程执行 LZ77 压缩 - 查找编码为(匹配长度,距离)对的输入的重复部分,而主线程处理 LZ77 阶段的输出(生成霍夫曼代码, 使用这些代码对输入进行编码)。

效果很好,总体而言,它的运行速度提高了约30%,这非常酷。

线程对我来说非常新颖,我发现代码有点吓人,我希望我的锁和记忆障碍是正确的。它似乎工作正常,但我认为很容易出现一个隐藏的并发错误,该错误可能不会出现在测试中。

和以前一样,代码的副本在这里:https://github.com/georgebarwood/pdf/blob/master/Deflator.cs

最新更新