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

自动并行化

  •  11
  • DuduAlul  · 技术社区  · 16 年前

    请看下面的代码:

    for(int i=0;i<100;i++)
       sum1 += rand(100)
    for(int j=0;j<100;j++)
       sum2 += rand(100)/2
    

    你觉得有可能吗? 我有一种感觉,理论上这是不可能的(它提醒我停止的问题),但我不能证明这个想法。

    你认为这是一个有用的项目吗?有类似的吗?

    6 回复  |  直到 16 年前
        1
  •  1
  •   waxwing    16 年前

    在一般情况下,是否有可能知道一段代码是否可以并行化并不重要,因为即使您的算法不能检测到所有可以并行化的情况,也可能检测到其中一些情况。

    这并不意味着它会有用。考虑以下几点:

    1. 首先,要在编译时执行此操作,必须检查要并行化的构造中可能到达的所有代码路径。除了简单的计算,这对任何事情来说都可能是棘手的。
    2. 即使你能做到这一点,最终也会让用户感到困惑。很难解释为什么他的代码不可并行化,以及应该如何更改。

    我认为,如果您想在Java中实现这一点,您需要更多地将其作为一个库来编写,并让用户决定并行化什么(库函数和注释一起?只是大声思考)。函数式语言更适合这种情况。

    作为一件琐事:在并行编程过程中,我们必须检查代码并决定它是否可并行。我记不清“最多一次”财产的具体情况了?有人告诉我吗?),但这个故事的寓意是,即使是看似微不足道的案件,也极为困难。

        2
  •  13
  •   Karmastan    16 年前

    这称为自动并行化。如果你正在寻找一些程序,你可以用它来为你做这件事,它还不存在。但最终还是有可能的。这是一个难题,也是一个积极研究的领域。如果你还好奇。。。

    可以自动将示例分割为多个线程,但不能按照您的思维方式。当前的一些技术尝试运行 对于 -循环完成后,可以启动下一个循环。其他技术可能会变得更疯狂,执行 i++ 一个线程的增量和 rand() 在一个单独的线程上。

    正如其他人指出的,迭代之间存在真正的依赖关系,因为 具有内部状态。这本身并不妨碍并行化。编译器可以识别内存依赖关系和 可以从一个线程转发到另一个线程。但它可能会将您限制为只有几个并行线程。如果没有依赖项,您可以在尽可能多的内核上运行它。

    如果你真的对这个话题感兴趣,并且不介意筛选研究论文:

    1. Automatic thread extraction with decoupled software pipelining (2005)G。奥托尼。
    2. Speculative parallelization using software multi-threaded transactions
        3
  •  6
  •   Reed Copsey    16 年前

    这几乎是不可能的。

    问题是,为了有效地并行化,您需要提前知道比编译器甚至运行时容易获得的更多的信息。

    rand() 是线程安全的-而许多随机数生成例程不是(爪哇的 Math.random() 为您同步-但是。)

    尝试进行这种类型的自动并行化,至少在这一点上,对于任何“真正的”应用程序都是不实际的。

        4
  •  5
  •   Andrew    16 年前

    这当然是可能的,但这是一项极其艰巨的任务。几十年来,这一直是编译器研究的重点。最基本的问题是,我们无法创建一个工具来为java代码找到最佳的线程分区(这相当于停止问题)。

    另一个简化方法是减少试图保持忙碌的并行单元的数量。如果你把这两个简化放在一起,那么你就得到了最先进的自动矢量化(一种用于生成MMX/SSE风格代码的特定类型的并行化)。到那个阶段已经花了几十年的时间,但如果你看看英特尔这样的编译器,那么性能开始变得相当不错。

    对于您的特定示例,如果您假设rand()是一个并行版本,因此您可以独立于不同的线程调用它,那么很容易看到代码可以分成两部分。编译器只需进行依赖性分析,就可以确定两个循环都不使用另一个循环的数据,也不影响另一个循环。因此,在用户级代码中,它们之间的顺序是一个错误的依赖关系,可能会被拆分(即将每个线程放在一个单独的线程中)。

    sum1 = (((rand_0 + rand_1) + rand_2) + rand_3) ....
    sum1 = (rand_0 + rand_1) + (rand_2 + rand_3) ...
    

    第二种方法的优点是括号中的每一个加法都可以与其他加法并行计算。一旦你有50个结果,然后他们可以被合并成进一步的25个补充,以此类推。。。这样做的工作量更多,50+25+13+7+4+2+1=102,而原来是100,但只有7个连续步骤,因此除了并行分叉/连接和通信开销外,它的运行速度快了14倍。这种加法树在并行体系结构中被称为聚集操作,它往往是计算的昂贵部分。

    总而言之 :要做到完美是不可能的,要做好是非常困难的,有很多积极的研究来找出我们能做多少。

        5
  •  1
  •   Amber    16 年前

    有些项目试图简化并行化,例如 Cilk . 然而,它并不总是工作得那么好。

        6
  •  0
  •   mobileappDev Seva Alekseyev    6 年前

    我了解到,从JDK1.8(Java8)开始,您可以使用parallelStream()利用/利用CPU的多个内核,以防流使用。

    为什么?/原因是:当操作需要自动取消/装箱时,并行流的性能可能会比顺序流的性能差得多。对于这些场景,建议使用java8原语流,如IntStream、LongStream和DoubleStream。

        7
  •  -1
  •   Deathlymad    11 年前

    编程语言是Java,Java是一个虚拟机。所以不能在运行时在VM拥有的不同线程上执行代码。因为所有的内存等都是这样处理的,所以不会造成任何损坏。您可以将代码视为估计执行时间的指令堆栈,然后将其分发到线程数组中,每个线程的执行堆栈的时间大致相同。这可能是危险的,尽管像OpenGL立即模式这样的一些图形需要保持秩序,而且大多数情况下根本不应该被线程化。