使用 MPI_Gather openmpi c 收集字符串

Gathering strings with MPI_Gather openmpi c

我想为每个进程生成一个字符串,然后收集所有内容。但是在每个进程中创建的字符串都是通过附加 int 和 char 创建的。

我仍然无法正确收集所有内容。我可以一个一个地打印所有部分字符串,但是如果我尝试打印 rcv_string,我只会得到一个部分字符串或者可能是一个分段错误。

我试过用 memset 在字符串末尾置零,为字符串动态和静态保留内存,...但我找不到方法。

如果有人知道如何初始化字符串并正确地进行收集以实现 objective,那就太好了。

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

    int rank;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    char *string;        // ????????????
    char *rcv_string;    // ????????????

    if (rank == 0)  {
        sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0);
    }
    else if (rank == 1) {
        sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0);
    }
    else if (rank == 2) {
        sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0);
    }
    else if (rank == 3) {
        sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0);
    }
    else if (rank == 4) {
        sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0);
    }
    else if (rank == 5) {
        sprintf(string+strlen(string), "%dr%dg%db%dl\n",255,255,255,0);
    }

    MPI_Gather(string,???,MPI_CHAR,rcv_string,???,MPI_CHAR,0,MPI_COMM_WORLD);

    if (rank == 0) {
        printf("%s",rcv_string);
    }

    MPI_Finalize();
    return 0;
}

尝试以下操作:

#define MAX_STR_LEN 100

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

    int rank, size;
    MPI_Init(&argc, &argv);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);
    MPI_Comm_size(MPI_COMM_WORLD, &size);

    char string[MAX_STR_LEN] = "some string";

    char *rcv_string = NULL;
    if (rank == 0) {
        // Only the master needs to allocate the memory
        // for the result string which needs to be large
        // enough to contain the input strings from `size`
        // peers.
        rcv_string = malloc(MAX_STR_LEN * size);
    }

    ...same code...

    MPI_Gather(string, strlen(string), MPI_CHAR,
               rcv_string, MAX_STR_LEN, MPI_CHAR, 0, MPI_COMM_WORLD);

    if (rank == 0) {
        printf("%s",rcv_string);
        free(rcv_string);
    }

    MPI_Finalize();
    return 0;
}

运行 此代码与 mpirun -n 5 ./a.out 生成以下内容:

some string255r255g255b0l
some string255r255g255b0l
some string255r255g255b0l
some string255r255g255b0l
some string255r255g255b0l

确保定义 MAX_STR_LEN 以使其足以满足您的要求。如果该值变大,您可能需要考虑堆分配(即 malloc)。

我设法重现了只打印了一个部分字符串的错误行为。

这与您使用sprintf有关。

C 如何处理 char 数组?

在 C 中使用数组时,必须首先为其分配内存。动态或静态,没关系。假设你为 10 char 分配了足够的内存。

char my_string[10];

没有初始化,里面是无意义的字符。

让我们假设 my_string 包含 "qwertyuiop".

假设您要用字符串 foo 填充 my_string。您使用 sprintf.

sprintf(my_string, "foo");

C如何用3个字符填充10个槽位?

它用 3 个字符填充前 3 个槽位。然后,它用 "end of string" 字符填充第 4 个槽。这由 '[=21=]' 表示,它在通过编译器时被转换为 "end of string" 字符。

因此,在您的命令之后,my_string 包含 "foo[=23=]tyuiop"。如果打印出my_string,C就知道不要打印出[=25=].

后面的无意义字符

这与 MPI_Gather 有什么关系?

MPI_Gather收集不同进程的数组,在一个进程上全部放到一个数组中

如果您在进程 0 上有 "foo[=23=]tyuiop" 并且在进程 1 上有 "bar[=29=]ghjkl;",它们将合并为 "foo[=30=]tyuiopbar[=30=]ghjkl;"

如您所见,进程 1 的数组出现在进程 0 的 "end of line" 字符之后。C 会将进程 1 的所有字符视为无意义。

一个不完整的解决方案

与其尝试一次打印所有 rcv_string,不如承认有 "end of string" 个字符分散在各处。然后,根据它来自的过程,打印出"start of string"位置不同的字符串。

#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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

  int rank, size;
  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &size);

  int part_str_len = 18;

  char *my_string;
  char *rcv_string;

  if ((my_string = malloc(part_str_len*sizeof(char))) == NULL){
    MPI_Abort(MPI_COMM_WORLD,1);
  }
  if ((rcv_string = malloc(part_str_len*size*sizeof(char))) == NULL){
    MPI_Abort(MPI_COMM_WORLD,1);
  }

  sprintf(my_string, "%dr%dg%db%dl\n",255,255,255,0);

  MPI_Gather(my_string,18,MPI_CHAR,rcv_string,18,MPI_CHAR,0,MPI_COMM_WORLD);

  if (rank == 0) {
    printf("%s",rcv_string);
  }

  char *cat_string;
  if ((cat_string = malloc(part_str_len*size*sizeof(char))) == NULL){
    MPI_Abort(MPI_COMM_WORLD,1);
  }

  if (rank == 0){
    int i;
    sprintf(cat_string, "%s", rcv_string);
    for (i = 1; i < size; i++){
      strcat(cat_string, &rcv_string[part_str_len*i]);
    }
  }

  if (rank == 0) {
    printf("%s",cat_string);
  }

  free(my_string);
  free(rcv_string);
  free(cat_string);

  MPI_Finalize();
  return 0;
}