代码之家  ›  专栏  ›  技术社区  ›  MusiGenesis

关于多线程、锁和多核处理器的多部分问题(Multi^3)

  •  2
  • MusiGenesis  · 技术社区  · 15 年前

    void Blend(int[] dest, int[] src, int offset)
    {
        for (int i = 0; i < src.Length; i++)
        {
            int rdr = dest[i + offset];
            dest[i + offset] = src[i] > rdr? src[i] : rdr; 
        }
    }
    

    第二种方法创建两组独立的 int 数组并遍历它们,这样一个集合的每个数组 Blend 与另一组中的每个数组合并,如下所示:

    void CrossBlend()
    {
        int[][] set1 = new int[150][75000]; // we'll pretend this actually compiles
        int[][] set2 = new int[25][10000]; // we'll pretend this actually compiles
        for (int i1 = 0; i1 < set1.Length; i1++)
        {
            for (int i2 = 0; i2 < set2.Length; i2++)
            {
                Blend(set1[i1], set2[i2], 0); // or any offset, doesn't matter
            }
        }
    }
    

    第一个问题:

    如果没有,这会不会:

    void Blend(int[] dest, int[] src, int offset)
    {
        lock (dest)
        {
            for (int i = 0; i < src.Length; i++)
            {
                int rdr = dest[i + offset];
                dest[i + offset] = src[i] > rdr? src[i] : rdr; 
            }
        }
    }
    

    是有效的解决方法吗?

    如果是这样的话,使用这样的锁可能会有什么性能成本?我假设在这样的情况下,如果一个线程试图锁定当前被另一个线程锁定的目标数组,那么第一个线程将阻塞,直到释放锁为止,而不是继续处理某些内容。

    第三个问题: 如何以多线程的方式利用多核处理器的优势来解决这个问题(这是基于一个潜在的错误假设,即多线程解决方案不会在单核处理器上加速此操作)?我猜我希望每个核心运行一个线程,但我不知道这是不是真的。

    1 回复  |  直到 15 年前
        1
  •  3
  •   mdma    15 年前

    与CrossBlend的潜在争用是set1—混合的目标。与其使用锁(与所做的工作量相比,锁会相对昂贵),不如安排每个线程在自己的目标上工作。即给定的目标(set1中某个索引处的数组)由给定的任务拥有。这是可能的,因为结果独立于交叉贷款处理数组的顺序。

    然后,每个任务应该只运行CrossBlend中的内部循环,并使用要使用的dest数组(set1)的索引(或索引范围)对任务进行参数化

    您还可以并行化Blend方法,因为每个索引都是独立于其他索引计算的,因此没有争用。但在今天的机器上,有<40核你将得到足够的平行只要线程交叉方法。

    要在多核上有效运行,您可以

    1. 对于N个核心,将问题分为N个部分。假设set1与内核的数量相比相当大,您可以将set1划分为N个范围,并将每个范围的索引传递到运行内部交叉线程循环的N个线程中。这将提供相当好的并行性,但这不是最佳的(有些线程会很快完成,最后没有工作要做。)
    2. 一个更复杂的方案是使交叉内部循环的每次迭代成为一个单独的任务。拥有N个队列(对于N个核心),并在队列之间分配任务。启动N个线程,每个线程从一个队列中读取它的任务。如果线程队列变为空,它将从其他线程的队列中获取任务。

    第二种方法最适合于大小不规则的任务,或者系统用于其他任务的情况,因此某些核心可能在其他进程之间进行时间切换,因此不能期望在不同的核心上在大致相同的时间内完成相同数量的工作。

    第一种方法编写代码要简单得多,并且可以提供良好的并行性。