Code of the Week #27: reduce()

#201914

大家经常会在程序中见到 reduce(),比如在 applications/solvers/basic/potentialFoam/potentialFoam.C

                // Calculate mean velocity
                scalar u = sum(mag(Upatch));
                label patchSize = Upatch.size();

                reduce(u, sumOp<scalar>());
                reduce(patchSize, sumOp<label>());

这个是什么意思呢?

在并行计算中,每个语句都会在各进程执行,对于 scalar u = sum(mag(Upatch)) 这一句,就是把 patch 上的速度值相加,每个进程上都会得到一个 u ,因此,这是有问题的,我们希望的是有一个最后的 u,而现在每个线程都有一个。所以存在合并问题。这个在 OpenFOAM 的并行计算中,就用 reduce() 来解决。

实际上,reduce 在并行计算 (MPI) 中,叫归约,是指在分布在不同进程中的数据间进行交互的运算,常用的运算有求和、求最大或最小值等。

当然,不必对并行计算要有深入的了解才能使用。在 OpenFOAM 中,提供了这样的操作模板,见
src/foam/db/IOstreams/Pstreams/PstreamReduceOps.H

// Reduce operation with user specified communication schedule
template <class T, class BinaryOp>
void reduce
(
    const List<Pstream::commsStruct>& comms,
    T& Value,
    const BinaryOp& bop
)
{
    Pstream::gather(comms, Value, bop); // 将各进程的 Value 按 bop 操作归约到 0 进程/主进程
    Pstream::scatter(comms, Value);     // 将 Value 发送给所有进程
}

// Reduce using either linear or tree communication schedule
template <class T, class BinaryOp>
void reduce
(
    T& Value,
    const BinaryOp& bop
)
{
    if (Pstream::nProcs() < Pstream::nProcsSimpleSum)
    {
        reduce(Pstream::linearCommunication(), Value, bop);
    }
    else
    {
        reduce(Pstream::treeCommunication(), Value, bop);
    }
}

因此,reduce() 既作了归约,也做了分发的工作,使所有进程可获得归约的结果。