TPL - ExecutionDataflowBlockOptions定义BoundedCapacity降低性能

时间:2016-12-22 20:20:59

标签: c# performance parallel-processing performance-testing tpl-dataflow

有什么办法可以通过TPL限制来限制性能下降吗?

我有一个复杂的组件管道,并试图限制所需的内存要求。我从多个文件并行读取,管道中的组件可能会从这些文件的随机部分读取一些内容,其余组件执行CPU绑定操作。

我使用通用的测试方法将性能测试平台简化为这些测试。

private void TPLPerformaceTest(int generateNumbers, ExecutionDataflowBlockOptions transformBlockOptions)
{
    var transformBlock = new TransformBlock<int, int>(i => i, transformBlockOptions);

    var storedCount = 0;
    var generatedCount = 0;
    var store = new ActionBlock<int>(i => Interlocked.Increment(ref storedCount));

    transformBlock.LinkTo(store);
    transformBlock.Completion.ContinueWith(_ => store.Complete());

    for (int i = 0; i < generateNumbers; i++)
    {
        transformBlock.SendAsync(i).Wait(); //To ensure delivery
        Interlocked.Increment(ref generatedCount);
    }
    transformBlock.Complete();
    store.Completion.Wait();

    Assert.IsTrue(generatedCount == generateNumbers);
    Assert.IsTrue(storedCount == generateNumbers);
}

第一个无限制。在我的CPU上完成需要 12s ,消耗大约 800MB 的RAM,平均CPU利用率大约 35%

[Test]
public void TPLPerformaceUnlimitedTest()
{
    var generateNumbers = 100000000;
    this.TPLPerformaceTest(generateNumbers,new ExecutionDataflowBlockOptions());
}

第二次测试只是将 BoundedCapacity设置为int.MaxValue ,因此完全没有限制,需要 20-30s 才能完成,消耗 2.1GB 50%。根据手册,BoundedCapacity默认设置为int.MaxValue,所以我没有看到性能下降的原因。

[Test]
[Sequential]
public void TPLPerformaceBounedCapacityTest()
{
    var generateNumbers = 100000000;
    this.TPLPerformaceTest(generateNumbers,new ExecutionDataflowBlockOptions()
      { BoundedCapacity = Int32.MaxValue });
}

第三个测试限制 BoundedCapacity to generateNumbers / 1000 ,ergo 100000.完成需要60秒,消耗 450MB 的RAM,平均CPU利用率约为 60 %

[Test]
[Sequential]
public void TPLPerformaceBounedCapacityTenthTest()
{
    var generateNumbers = 100000000;
    this.TPLPerformaceTest(generateNumbers,new ExecutionDataflowBlockOptions()
      { BoundedCapacity = generateNumbers / 1000 });
}

第四次测试将 MaxDegreeOfParallelism限制为-1 ,这是根据手册无限制。它消耗了 27GB 的RAM,平均CPU利用率大约 85%,并且还没有完成 5分钟

[Test]
[Sequential]
public void TPLPerformaceMaxDegreeOfParallelismTest()
{
    var generateNumbers = 100000000;
    this.TPLPerformaceTest(generateNumbers, new ExecutionDataflowBlockOptions()
      { MaxDegreeOfParallelism = -1 });
}

所有方法似乎都很难影响性能,并且由于我的合理期望而无法表现。

1 个答案:

答案 0 :(得分:5)

<击> 由于这一行你正在降低性能:

transformBlock.SendAsync(i).Wait(); //To ensure delivery

阻止当前线程在传递完成之前。您应切换到await以释放线程以继续执行其他任务:

await transformBlock.SendAsync(i); //To ensure delivery

<击>

更新

我对你的话感到困惑

  

根据手册,BoundedCapacity默认设置为int.MaxValue

因为事实并非如此,from official documentation

  

BoundedCapacity
  System.Threading.Tasks.Dataflow.dll中包含的大多数数据流块都支持有界容量的规范   这是块可能存储并且在任何时间都在飞行中的物品数量的限制   默认情况下,此值初始化为DataflowBlockOptions.Unbounded-1),表示没有限制。

在此处,您可以在运行此代码后看到所有默认值:

var options = new ExecutionDataflowBlockOptions();

enter image description here

所以将BoundedCapacity设置为int.MaxValue 的第二次测试会添加一个限制,这会在块的缓冲区中添加一些位置可用性检查。

你可以在第三次测试中看到类似的行为,它比第二次测试消耗的内存少得多,但是对缓冲区做更多的检查和等待时间来释放空间,所以它的工作速度较慢,但​​需要很少的内存来分配。

此外,您可以在屏幕截图中看到MaxDegreeOfParallelism等于1

  

MaxDegreeOfParallelism
  默认情况下,单个数据流块一次只处理一条消息,对所有尚未处理的消息进行排队,以便在当前处理消息完成时对它们进行处理。

将此参数设置为-1后,打开潘多拉魔盒,因为所有消息都由同一任务调度程序同时执行 ,再次according to the documentation

  

如果设置为DataflowBlockOptions.Unbounded-1),则可以同时处理任意数量的消息,最大值由数据流块所针对的基础调度程序自动管理。

正如我在内存消耗中看到的那样,任务调度程序决定为每个消息启动新线程,因为线程池中没有可用的消息,每个消息大约需要1MB,所以你有一个大约{{ {1}}线程互相争夺CPU时间。正如你所看到的,他们并不擅长这一点 建议的并行度通常为27000,因此,如果您想加快一个块的速度,可以将Environment.ProcessorCount设置为此属性。但是,在更复杂的情况下,这可能并不总是最佳选择,因为其他块将在等待CPU时间时停止。

那么你的MaxDegreeOfParallelism是什么?