MPI_Bcast - процесс получает от root до корневых передач? [Дубликат]

Как отметил @FelixKling, наиболее вероятным сценарием является то, что узлы, которые вы ищете, еще не существуют.

Однако современные методы разработки часто могут манипулировать элементами документа за пределами дерева документов либо с DocumentFragments, либо просто отсоединением / повторным подключением текущих элементов напрямую. Такие методы могут использоваться как часть шаблонов JavaScript или для предотвращения чрезмерных операций перерисовки / переплавки, в то время как элементы, о которых идет речь, сильно изменяются.

Аналогично, новая функциональность «Теневой DOM» развертывается в современных браузерах позволяет элементам быть частью документа, но не обрабатываться запросом document.getElementById и всеми его методами sibling (querySelector и т. д.). Это делается для инкапсуляции функциональных возможностей и, в частности, скрыть его.

Опять же, скорее всего, элемент, который вы ищете, просто (пока) в документе, и вы должны сделать, как предлагает Феликс , Тем не менее, вы также должны знать, что это все чаще является не единственной причиной того, что элемент может быть необоснованным (временно или постоянно).

2
задан Κωστας Ιωαννου 10 July 2013 в 13:56
поделиться

4 ответа

Невозможно гарантировать, что сообщения из многих разных процессов поступят в «правильном» порядке, когда они прибудут в другой процесс. Это, по сути, то, что здесь происходит.

Даже если вы явно не отправляете сообщения, когда вы печатаете что-то на экране, оно должно быть отправлено процессу в вашей локальной системе (mpiexec или mpirun), где он может быть напечатан на экране. MPI не знает, что такое правильный порядок для этих сообщений, поэтому он просто печатает их по мере их поступления.

Если вам требуется, чтобы ваши сообщения печатались в определенном порядке, вы должны отправить их всем до одного ранга, который может печатать их в любом порядке. Пока один ранг выполняет всю печать, все сообщения будут упорядочены правильно.

Следует сказать, что, вероятно, будут ответы, которые вы можете найти там, в которых говорится, что вы можете поместить новую строку в конце строки или используйте команду flush (), чтобы убедиться, что буферы сброшены, но это не гарантирует упорядочение на удаленном конце по причинам, упомянутым выше.

5
ответ дан Wesley Bland 19 August 2018 в 18:27
поделиться
  • 1
    +1; fflush(stdout), безусловно, стоит попробовать, но да, нет никакой гарантии, и то, что работает в одной системе, может быть не на другом. – Jonathan Dursi 10 July 2013 в 15:38

Я был вдохновлен ответом Святослава Павленко: использование блокирующих MPI-коммуникаций для обеспечения последовательного вывода. В то время как Уэсли Блэнд указывает, что MPI не будет построен для последовательного выхода. Поэтому, если мы хотим выводить данные, имеет смысл либо иметь каждый выходной процессор (не сталкивающийся). В качестве альтернативы, если порядок данных важен (и он не слишком большой), рекомендуемый подход заключается в том, чтобы отправить все это на процессор (скажем, ранг 0), который затем правильно форматирует данные.

Мне , это, кажется, немного перебор, особенно когда данные могут быть строками переменной длины, что слишком часто является тем, что std::cout << "a=" << some_varible << " b=" << some_other_variable часто. Поэтому, если мы хотим получить скорую и грязную печать в порядке, мы можем использовать ответ Святослав Павленко для создания потока последовательного вывода. Это решение работает отлично, но его производительность сильно ухудшается со многими процессорами, поэтому не используйте его для вывода данных!

#include <iostream>
#include <sstream>
#include <mpi.h>

MPI Удержание:

int mpi_size;
int mpi_rank;

void init_mpi(int argc, char * argv[]) {
    MPI_Init(& argc, & argv);
    MPI_Comm_size(MPI_COMM_WORLD, & mpi_size);
    MPI_Comm_rank(MPI_COMM_WORLD, & mpi_rank);
}

void finalize_mpi() {
    MPI_Finalize();
}

Общее класс -purpose, который позволяет цепочку сообщений MPI

template<class T, MPI_Datatype MPI_T> class MPIChain{
    // Uses a chained MPI message (T) to coordinate serial execution of code (the content of the message is irrelevant).
    private:
        T message_out; // The messages aren't really used here
        T message_in;
        int size;
        int rank;

    public:
        void next(){
            // Send message to next core (if there is one)
            if(rank + 1 < size) {
            // MPI_Send - Performs a standard-mode blocking send.
            MPI_Send(& message_out, 1, MPI_T, rank + 1, 0, MPI_COMM_WORLD);
            }
        }

        void wait(int & msg_count) {
            // Waits for message to arrive. Message is well-formed if msg_count = 1
            MPI_Status status;

            // MPI_Probe - Blocking test for a message.
            MPI_Probe(MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, & status);
            // MPI_Get_count - Gets the number of top level elements.
            MPI_Get_count(& status, MPI_T, & msg_count);

            if(msg_count == 1) {
                // MPI_Recv - Performs a standard-mode blocking receive.
                MPI_Recv(& message_in, msg_count, MPI_T, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, & status);
            }
        }

        MPIChain(T message_init, int c_rank, int c_size): message_out(message_init), size(c_size), rank(c_rank) {}

        int get_rank() const { return rank;}
        int get_size() const { return size;}
};

Теперь мы можем использовать наш класс MPIChain для создания нашего класса, который управляет потоком вывода:

class ChainStream : public MPIChain<int, MPI_INT> {
    // Uses the MPIChain class to implement a ostream with a serial operator<< implementation.
    private:
        std::ostream & s_out;

    public:
        ChainStream(std::ostream & os, int c_rank, int c_size)
            : MPIChain<int, MPI_INT>(0, c_rank, c_size), s_out(os) {};

        ChainStream & operator<<(const std::string & os){
            if(this->get_rank() == 0) {
                this->s_out << os;
                // Initiate chain of MPI messages
                this->next();
            } else {
                int msg_count;
                // Wait untill a message arrives (MPIChain::wait uses a blocking test)
                this->wait(msg_count);
                if(msg_count == 1) {
                    // If the message is well-formed (i.e. only one message is recieved): output string
                    this->s_out << os;
                    // Pass onto the next member of the chain (if there is one)
                    this->next();
                }
            }

            // Ensure that the chain is resolved before returning the stream
            MPI_Barrier(MPI_COMM_WORLD);

            // Don't output the ostream! That would break the serial-in-time exuction.
            return *this;
       };
};

Обратите внимание на MPI_Barrier в конце operator<<. Это делается для того, чтобы код не начинал вторую цепочку вывода. Несмотря на то, что это можно было бы перемещать за пределы operator<<, я решил, что я бы поставил его здесь, так как это все равно предполагается серийным выходом ....

Объединяя все это:

int main(int argc, char * argv[]) {
    init_mpi(argc, argv);

    ChainStream cs(std::cout, mpi_rank, mpi_size);

    std::stringstream str_1, str_2, str_3;
    str_1 << "FIRST:  " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl;
    str_2 << "SECOND: " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl;
    str_3 << "THIRD:  " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl;

    cs << str_1.str() << str_2.str() << str_3.str();
    // Equivalent to:
    //cs << str_1.str();
    //cs << str_2.str();
    //cs << str_3.str();

    finalize_mpi();
}

Обратите внимание, что мы конкатенируем строки str_1, str_2, str_3 перед , мы отправляем им экземпляр ChainStream. Обычно можно было бы сделать что-то вроде:

std::cout << "a" << "b" << "c"" << std::endl

, но это применимо operator<< слева направо, и мы хотим, чтобы строки были готовы к выходу перед последовательным запуском через каждый процесс.

g++-7 -O3 -lmpi serial_io_obj.cpp -o serial_io_obj
mpirun -n 10 ./serial_io_obj

Выходы:

FIRST:  MPI_SIZE = 10 RANK = 0
FIRST:  MPI_SIZE = 10 RANK = 1
FIRST:  MPI_SIZE = 10 RANK = 2
FIRST:  MPI_SIZE = 10 RANK = 3
FIRST:  MPI_SIZE = 10 RANK = 4
FIRST:  MPI_SIZE = 10 RANK = 5
FIRST:  MPI_SIZE = 10 RANK = 6
FIRST:  MPI_SIZE = 10 RANK = 7
FIRST:  MPI_SIZE = 10 RANK = 8
FIRST:  MPI_SIZE = 10 RANK = 9
SECOND: MPI_SIZE = 10 RANK = 0
SECOND: MPI_SIZE = 10 RANK = 1
SECOND: MPI_SIZE = 10 RANK = 2
SECOND: MPI_SIZE = 10 RANK = 3
SECOND: MPI_SIZE = 10 RANK = 4
SECOND: MPI_SIZE = 10 RANK = 5
SECOND: MPI_SIZE = 10 RANK = 6
SECOND: MPI_SIZE = 10 RANK = 7
SECOND: MPI_SIZE = 10 RANK = 8
SECOND: MPI_SIZE = 10 RANK = 9
THIRD:  MPI_SIZE = 10 RANK = 0
THIRD:  MPI_SIZE = 10 RANK = 1
THIRD:  MPI_SIZE = 10 RANK = 2
THIRD:  MPI_SIZE = 10 RANK = 3
THIRD:  MPI_SIZE = 10 RANK = 4
THIRD:  MPI_SIZE = 10 RANK = 5
THIRD:  MPI_SIZE = 10 RANK = 6
THIRD:  MPI_SIZE = 10 RANK = 7
THIRD:  MPI_SIZE = 10 RANK = 8
THIRD:  MPI_SIZE = 10 RANK = 9
1
ответ дан Johannes Blaschke 19 August 2018 в 18:27
поделиться

В стандарте MPI не указывается, как следует собирать stdout из разных узлов, а fflush не помогает.

Если вам нужно печатать большие выходы по порядку, возможно, лучшим решением не является собрать их все и распечатать сразу, потому что это приведет к генерации трафика по сети. Лучшее решение - создать нечто похожее на виртуальное кольцо, где каждый процесс ждет токен из предыдущего процесса, печатает и отправляет токен на следующий. Конечно, первый процесс не должен ждать, он печатает и отправляет на следующий.

В любом случае в случае действительно большого выхода, где, вероятно, нет смысла печатать выходы на видео, вы должны используйте MPI-IO, как предложил Джонатан Дурси.

0
ответ дан Nicola 19 August 2018 в 18:27
поделиться

Итак, вы можете сделать что-то вроде этого:

MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
    MPI_Send(&message, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
    printf("1 SIZE = %d RANK = %d MESSAGE = %d \n",size,rank, message);
} else {
    int buffer;
    MPI_Status status;
    MPI_Probe(MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status);
    MPI_Get_count(&status, MPI_INT, &buffer);
    if (buffer == 1) {
        printf("2 SIZE = %d RANK = %d MESSAGE = %d \n",size,rank, message);
        MPI_Recv(&message, buffer, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status);
        if (rank + 1 != size) {
            MPI_Send(&message, 1, MPI_INT, ++rank, 0, MPI_COMM_WORLD);
        }
    };
};
MPI_Finalize();

После выполнения:

$ mpirun -n 5 ./a.out 
1 SIZE = 5 RANK = 0 MESSAGE = 999 
2 SIZE = 5 RANK = 1 MESSAGE = 999 
2 SIZE = 5 RANK = 2 MESSAGE = 999 
2 SIZE = 5 RANK = 3 MESSAGE = 999 
2 SIZE = 5 RANK = 4 MESSAGE = 999 
1
ответ дан Святослав Павленко 19 August 2018 в 18:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: