【问题标题】:MPI_Bcast error on multiple nodes多个节点上的 MPI_Bcast 错误
【发布时间】:2014-07-06 11:50:49
【问题描述】:

背景:我正在编写基于collfs 项目的 I/O 系统调用的 MPI 版本。

代码在单个节点上的多个处理器上运行而不会出错。

但是,在多个节点上运行会导致分段错误...带有 2 个进程,每个节点 1 个进程的错误消息如下:

$ qsub test.sub $ cat test.e291810 0: pasc_open(./libSDL.so, 0, 0) 1: pasc_open(./libSDL.so, 0, 0) 1: mptr[0]=0 mptr[len-1]=0 1: MPI_Bcast(mptr=eed11000, len=435104, MPI_BYTE, 0, MPI_COMM_WORLD) 0: mptr[0]=127 mptr[len-1]=0 0: MPI_Bcast(mptr=eeb11000, len=435104, MPI_BYTE, 0, MPI_COMM_WORLD) _pmiu_daemon(SIGCHLD): [NID 00632] [c3-0c0s14n0] [Sun May 18 13:10:30 2014] PE RANK 0 exit signal Segmentation fault [NID 00632] 2014-05-18 13:10:30 Apid 8283706: initiated application termination

发生错误的函数如下:

static int nextfd = BASE_FD;
#define next_fd() (nextfd++)

int pasc_open(const char *pathname, int flags, mode_t mode)
{
    int rank;
    int err;

    if(!init)
        return ((pasc_open_fp) def.open)(pathname, flags, mode);

    if(MPI_Comm_rank(MPI_COMM_WORLD, &rank) != MPI_SUCCESS)
        return -1;
    dprintf("%d: %s(%s, %x, %x)\n", rank, __FUNCTION__, pathname, flags, mode);

    /* Handle just read-only access for now. */
    if(flags == O_RDONLY || flags == (O_RDONLY | O_CLOEXEC)) {
        int fd, len, xlen, mptr_is_null;
        void *mptr;
        struct mpi_buf { int len, en; } buf;
        struct file_entry *file;

        if(rank == 0) {
            len = -1;
            fd = ((pasc_open_fp) def.open)(pathname, flags, mode);
            /* Call stat to get file size and check for errors */
            if(fd >= 0) {
                struct stat st;
                if(fstat(fd, &st) >= 0)
                    len = st.st_size;
                else
                    ((pasc_close_fp) def.close)(fd);
            }
            /* Record them */
            buf.len = len;
            buf.en = errno;
        }
        /* Propagate file size and errno */
        if(MPI_Bcast(&buf, 2, MPI_INT, 0, MPI_COMM_WORLD) != MPI_SUCCESS)
            return -1;
        len = buf.len;
        if(len < 0) {
            dprintf("error opening file, len < 0");
            return -1;
        }
        /* Get the page-aligned size */
        xlen = page_extend(len);
        /* `mmap` the file into memory */
        if(rank == 0) {
            mptr = ((pasc_mmap_fp) def.mmap)(0, xlen, PROT_READ, MAP_PRIVATE,
                    fd, 0);
        } else {
            fd = next_fd();
            mptr = ((pasc_mmap_fp) def.mmap)(0, xlen, PROT_READ | PROT_WRITE,
                    MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
        }
        ((pasc_lseek_fp) def.lseek)(fd, 0, SEEK_SET);
        /* Ensure success on all aux. processes */
        if(rank != 0)
            mptr_is_null = !mptr;
        MPI_Allreduce(MPI_IN_PLACE, &mptr_is_null, 1, MPI_INT, MPI_LAND,
                MPI_COMM_WORLD);
        if(mptr_is_null) {
            if(mptr)
                ((pasc_munmap_fp) def.munmap)(mptr, xlen);
            dprintf("%d: error: mmap/malloc error\n", rank);
            return -1;
        }
        dprintf("%d: mptr[0]=%d mptr[len-1]=%d\n", rank, ((char*)mptr)[0], ((char*)mptr)[len-1]);
        /* Propagate file contents */
        dprintf("%d: MPI_Bcast(mptr=%x, len=%d, MPI_BYTE, 0, MPI_COMM_WORLD)\n",
        rank, mptr, len);
        if(MPI_Bcast(mptr, len, MPI_BYTE, 0, MPI_COMM_WORLD) != MPI_SUCCESS)
            return -1;
        if(rank != 0)
            fd = next_fd();
        /* Register the file in the linked list */
        file = malloc(sizeof(struct file_entry));
        file->fd = fd;
        file->refcnt = 1;
        strncpy(file->fn, pathname, PASC_FNMAX);
        file->mptr = mptr;
        file->len = len;
        file->xlen = xlen;
        file->offset = 0;
        /* Reverse stack */
        file->next = open_files;
        open_files = file;
        return fd;

    }
    /* Fall back to independent access */
    return ((pasc_open_fp) def.open)(pathname, flags, mode);
}

错误发生在最后的MPI_Bcast 调用中。我不知道为什么会发生这种情况:它从中复制的内存我可以取消引用。

我在运行 SUSE Linux x86_64 的自定义 Cray XC30 机器上使用 MPICH。

谢谢!


编辑:我尝试用 MPI_Send/MPI_Recv 对替换 MPI_Bcast 调用,结果是一样的。

【问题讨论】:

  • 在其他进程中调用 fd=next_fd() 时,您是否可能会陷入竞争状态?
  • 我不这么认为,因为fd 是线程相关的,而next_fd 被定义为全局的简单增量。
  • 进程生成后,每个进程中的全局变量都是相同的(那里有自己唯一的内存,所有内容都会被复制!)。如果你增加它并且不将它传递给所有其他进程,除了 rank 0 之外的每个进程都将具有相同的 fd,(当然 fd 的内存是进程唯一的)。
  • 文件描述符为每个线程单独增加和维护——希望我对代码所做的编辑清除了这一点。所以等级 0 将具有实际系统调用返回的“真实”fd,而其他将具有我生成的fd。由于fd 是特定于线程的,并且操作文件描述符的系统调用都被覆盖了,这仍然是个问题吗?
  • 如果您将 mmaped 数据 (mptr) 复制到 malloced 缓冲区会发生什么情况,您还会遇到段错误吗?

标签: c linux segmentation-fault mpi


【解决方案1】:

出于性能原因,Cray MPI 实现可能会发挥一些作用。在不了解内部情况的情况下,大部分答案都是猜测。

节点间通信可能不使用网络堆栈,而是依赖于某种共享内存通信。当您尝试通过网络堆栈发送 mmap-ed 缓冲区时,某处出现故障 - DMA 引擎(我在这里疯狂猜测)无法处理这种情况。

您可以尝试对 mmaped 缓冲区进行页面锁定 - 也许mlock 可以正常工作。 如果失败,则将数据复制到malloced 缓冲区。

【讨论】:

  • mlock 无法正常工作——我现在不得不使用malloc/memcpy 解决方案。
  • Cray uGNI 可能无法对此类缓冲区进行 RDMA,在这种情况下,它应该在内部进行复制,但由于它不这样做,因此您明确这样做的解决方法是正确的.
  • @Anycorn 节点间是一个错字。您的意思是节点内绕过网络堆栈。
猜你喜欢
  • 1970-01-01
  • 2012-11-05
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
  • 2014-07-24
  • 1970-01-01
  • 2012-11-20
相关资源
最近更新 更多