【问题标题】:C# parallel processing optionsC# 并行处理选项
【发布时间】:2017-06-01 21:26:44
【问题描述】:

我试图让我的一部分代码并行运行,我试图在 c# 中使用 ThreadPool 以避免任何头痛,但似乎我对池的要求太多了,我的代码实际上运行速度较慢它!

下面的代码解释了我想要做什么,我有许多音频样本(取决于从 512->4096 的任何地方的缓冲区大小)需要插入、传播并从节点读取(从 n = 4 到 n = 16) 在延迟网络中。这必须在逐个样本的基础上发生,所以我唯一的优化选择是参与插入/传播/读取的部分,并对网络中存在的每个节点进行并行操作。此操作在每个游戏帧中调用一次,并且在游戏的整个生命周期中都是必需的。查看分析器,散射操作需要大量时间,因此它是一个很好的候选者(我已经完成了传统优化)。目前,我让线程池工作者在一个工作项中为所有节点执行工作,只是为了启动和运行,但可以稍后拆分工作。

我认为下面代码的问题是工作项插入线程池的频率,我还在某处读到线程需要一段时间才能启动,所以如果线程池创建更多,它不一定有帮助。是否有人对其他并行处理方法有任何建议,或者可以发现我的线程池实现的任何错误?

public void propagateNetwork() {

    int numSampsToConsume = Mathf.min(inSamples.Count,buffersize);

    for (int i = 0; i < numSampsToConsume; i++) {
        outVal = 0.0f;
        inVal = inSamples.Dequeue () * networkInScale;

        directDelay.write (inVal);
        directVal = directDelay.read ();
        directVal *= directAtt;

        for (j = 0; j < network.Count; j++) {
            outVal += network [j].getOutgoing ();
            network [j].inputIncoming (inVal);
        }

        ThreadPool.QueueUserWorkItem (scatteringThreadPoolWrapper);
        scatteringThreadDone.WaitOne ();

        outVal += directVal;
        outSamples.Enqueue (outVal);
    }
}

public void scatteringThreadPoolWrapper(object threadConext) {
    doScatteringForNodeRange (0, network.Count);
}

public void doScatteringForNodeRange(int min,int max) {
    for (int i = min; i < max; i++) {
        network[i].doScattering (doLateReflections);
    }
    scatteringThreadDone.Set ();
}

【问题讨论】:

  • 如果你排队然后立即等待,那不是没有意义吗?您不应该将所有工作项排队,然后等待它们在另一个循环中完成吗?也许考虑用Parallel.for 替换你的外部?
  • 也许我误解了线程池的作用,但我假设我分配给它的每个任务都可能在不同的线程上。因此总的来说它会更快完成吗?我知道在示例代码中我还没有拆分工作,这只是一个测试,看看它是否有效。我需要等待,因为每个样本都必须在移动到下一个之前完成传播,出于同样的原因,并行 for 是不可能的。
  • 每个任务可能在不同的线程上,但是如果你将一个任务发送到另一个线程,然后在运行另一个任务之前等待在主线程上得到答案,那么你当然会变慢。您仍然一次只运行一个线程,但增加了创建线程和跨线程通信的开销。
  • 当然有道理。就像我说的,上面的实现并不是真正的并行版本。理想情况下,我会拆分要处理的节点,因此发送一半由线程池处理,其余在主线程中处理 - 等待以确保在下一个示例之前完成所有操作。开销的增加对我来说非常令人惊讶,分析器中代码的串行版本显示平均计算时间为 200 毫秒,而并行版本约为 1000 毫秒!
  • 您为此尝试过 TPL 数据流吗?

标签: c# multithreading parallel-processing threadpool


【解决方案1】:

也许使用Parallel.for 会起作用?我在每个循环迭代中都设置了一些局部变量,但我不知道这些操作都在做什么,以及如果并行运行会如何导致问题。

public void propagateNetwork() {
    int numSampsToConsume = Mathf.min(inSamples.Count,buffersize);

    Parallel.for(0, numSampsToConsume, i => {
        var outVal = 0.0f;
        var inVal = inSamples.Dequeue () * networkInScale;

        directDelay.write (inVal);
        var directVal = directDelay.read ();
        directVal *= directAtt;

        Parallel.for(0, network.Count, j => {
            outVal += network [j].getOutgoing ();
            network [j].inputIncoming (inVal);
        });

        doScatteringForNodeRange (0, network.Count);

        outVal += directVal;
        outSamples.Enqueue (outVal);
    });
}

【讨论】:

  • 刚刚实现了最里面的parallel for,还是比普通的for循环需要更长的时间!是否会为每个 i 创建新线程,与线程池不同,线程池试图重用池中的尽可能多的线程,并且只在需要时创建?我认为我的主要问题是启动新线程需要太长时间(如果这就是 parallel for 正在做的事情)最外面的循环不能并行,因为每个样本必须按顺序传播。
  • 减速可能来自并行操作的嵌套,毕竟 AFAIK 仍然是调用点的“同步”操作(例如,doScatteringForNodeRange 在内部循环完成之前不会运行) ,这实质上意味着每个外部循环都在管理并行状态,产生其他并行任务并暂停。理想情况下,您希望一个循环完成所有操作,因此:1. 看看您是否可以将内部循环扩展为主要驱动程序,以及 2. 避免任何共享状态访问。
  • 并行操作的嵌套是指这个asnwer中的2个并行吗?因为正如我之前提到的,我不能让外部并行,因此没有实现它。所有音频样本都必须按顺序处理,只有内部的操作是并行的候选者。但是你关于交换内部和外部循环的观点很有趣,我会考虑一下这是否可能!
  • 我对交换内部/外部循环有一个简短的想法。在算法的逻辑中这是不可能的,每个节点在每个时间步长(或样本传播)都依赖于来自其他每个节点的输入,因此它们会以这种方式不同步。
猜你喜欢
  • 1970-01-01
  • 2014-06-28
  • 2010-11-28
  • 2014-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多