MPI中的网络通信的原理,需要解决以下几个问题:

1. MPI使用什么网络协议进行通信?

2.中央数据库是存储在哪一台机器上?

3.集群中如果有一台机器挂掉了是否会影响其他机器?

参考: https://aosabook.org/en/openmpi.html

根据MCA, 每个框架下的模块是可变的,例如, btl (字节传输层)框架下有N多个网络协议模块:

既然是可变的,但是我们运行的时候都没有传入对应的选择参数,也就是说明有默认值。 官方文档也说了,工程师和科学家尽可能帮我们选择一个合理的默认值,但是对于不同的机器集群会有不同的效果,所以建议我们自己测试最好的参数。

当每个通信域(包括MPI_COMM_WORLD和MPI_COMM_SELF)被创建时,每个可用模块被询问是否需要在新通信域中使用。模块可以拒绝被使用,例如,一个基于共享内存的模块只有当通信域中的所有进程都在相同的物理节点上时,才允许被使用。通信域将会选择最高优先级的模块使用。

当然,这个也是可以让用户更改的

根据 https://aosabook.org/en/openmpi.html中介绍的,用户可以通过传入MCA命令行参数去改变运行时的模块

再看到位于 c/send.c 文件中的Send函数定义:

#if OMPI_BUILD_MPI_PROFILING
#if OPAL_HAVE_WEAK_SYMBOLS
#pragma weak MPI_Send = PMPI_Send
#endif
#define MPI_Send PMPI_Send
#endif static const char FUNC_NAME[] = "MPI_Send"; int MPI_Send(const void *buf, int count, MPI_Datatype type, int dest,
int tag, MPI_Comm comm)
{
int rc = MPI_SUCCESS; MEMCHECKER(
memchecker_datatype(type);
memchecker_call(&opal_memchecker_base_isdefined, buf, count, type);
memchecker_comm(comm);
); if ( MPI_PARAM_CHECK ) {
OMPI_ERR_INIT_FINALIZE(FUNC_NAME);
if (ompi_comm_invalid(comm)) {
return OMPI_ERRHANDLER_INVOKE(MPI_COMM_WORLD, MPI_ERR_COMM, FUNC_NAME);
} else if (count < ) {
rc = MPI_ERR_COUNT;
} else if (tag < || tag > mca_pml.pml_max_tag) {
rc = MPI_ERR_TAG;
} else if (ompi_comm_peer_invalid(comm, dest) &&
(MPI_PROC_NULL != dest)) {
rc = MPI_ERR_RANK;
} else {
OMPI_CHECK_DATATYPE_FOR_SEND(rc, type, count);
OMPI_CHECK_USER_BUFFER(rc, buf, type, count);
}
OMPI_ERRHANDLER_CHECK(rc, comm, rc, FUNC_NAME);
} if (MPI_PROC_NULL == dest) {
return MPI_SUCCESS;
} OPAL_CR_ENTER_LIBRARY();
rc = MCA_PML_CALL(send(buf, count, type, dest, tag, MCA_PML_BASE_SEND_STANDARD, comm));
OMPI_ERRHANDLER_RETURN(rc, comm, rc, FUNC_NAME);
}

前面一堆都是错误检查,会让不合法的操作不会真正的进行 send 这个操作。

最后看到关键的发送代码:

rc = MCA_PML_CALL(send(buf, count, type, dest, tag, MCA_PML_BASE_SEND_STANDARD, comm));

 MCA_PML_CALL 是一个宏,我们在 pml.h 中可以找到它:

#if MCA_ompi_pml_DIRECT_CALL

#include MCA_ompi_pml_DIRECT_CALL_HEADER

#define MCA_PML_CALL_STAMP(a, b) mca_pml_ ## a ## _ ## b
#define MCA_PML_CALL_EXPANDER(a, b) MCA_PML_CALL_STAMP(a,b)
#define MCA_PML_CALL(a) MCA_PML_CALL_EXPANDER(MCA_ompi_pml_DIRECT_CALL_COMPONENT, a)

#else
#define MCA_PML_CALL(a) mca_pml.pml_ ## a
#endif

由于 if 下代码块搜索不到,所以我们直接看 else 中的 mca_pml.pml_ send(buf, count, type, dest, tag, MCA_PML_BASE_SEND_STANDARD, comm)

其实 mca_pm 是一个导出的   mca_pml_base_module_t  变量:

OMPI_DECLSPEC extern mca_pml_base_module_t mca_pml;

  mca_pml_base_module_t  的定义如下:

struct mca_pml_base_module_1_0_1_t {

    /* downcalls from MCA to PML */
mca_pml_base_module_add_procs_fn_t pml_add_procs;
mca_pml_base_module_del_procs_fn_t pml_del_procs;
mca_pml_base_module_enable_fn_t pml_enable;
mca_pml_base_module_progress_fn_t pml_progress; /* downcalls from MPI to PML */
mca_pml_base_module_add_comm_fn_t pml_add_comm;
mca_pml_base_module_del_comm_fn_t pml_del_comm;
mca_pml_base_module_irecv_init_fn_t pml_irecv_init;
mca_pml_base_module_irecv_fn_t pml_irecv;
mca_pml_base_module_recv_fn_t pml_recv;
mca_pml_base_module_isend_init_fn_t pml_isend_init;
mca_pml_base_module_isend_fn_t pml_isend;
mca_pml_base_module_send_fn_t pml_send;
mca_pml_base_module_iprobe_fn_t pml_iprobe;
mca_pml_base_module_probe_fn_t pml_probe;
mca_pml_base_module_start_fn_t pml_start;
mca_pml_base_module_improbe_fn_t pml_improbe;
mca_pml_base_module_mprobe_fn_t pml_mprobe;
mca_pml_base_module_imrecv_fn_t pml_imrecv;
mca_pml_base_module_mrecv_fn_t pml_mrecv; /* diagnostics */
mca_pml_base_module_dump_fn_t pml_dump; /* FT Event */
mca_pml_base_module_ft_event_fn_t pml_ft_event; /* maximum constant sizes */
uint32_t pml_max_contextid;
int pml_max_tag;
int pml_flags;
};
typedef struct mca_pml_base_module_1_0_1_t mca_pml_base_module_1_0_1_t;
typedef mca_pml_base_module_1_0_1_t mca_pml_base_module_t;

  哦!!!这下明白了吧!!!我们所有的进行的 send , recv 等点对点 (PML) 的通信函数都封装在了这个结构体 的 函数指针成员里。

为什么要这么做呢?

——之前我们说过它支持不同的通信协议,在用户没有特定输入的时候,默认选择最高优先级的通信协议。如果每个通信协议都对应一套函数,那不是很麻烦???

为了让这个设计简单,可维护,用一个 base 封装起常见的操作,改变函数指针即可以改变使用的协议啦!!

那么下一节,我们就得看看,这个导出的   mca_pml_base_module_t  变量   mca_pm 的 函数指针在哪里初始化?——也就是,我们要看看它如何选择通信协议的!

OpenMPI源码剖析:网络通信原理(一)的更多相关文章

  1. OpenMPI源码剖析1:MPI_Init初探

    OpenMPI的底层实现: 我们知道,OpenMPI应用起来还是比较简单的,但是如果让我自己来实现一个MPI的并行计算,你会怎么设计呢?————这就涉及到比较底层的东西了. 回想起我们最简单的代码,通 ...

  2. OpenMPI源码剖析:网络通信原理(二) 如何选择网络协议?

    因为比较常用的是 TCP 协议,所以在 opal/mca/btl/tcp/btl_tcp.h 头文件中找到对应的 struct mca_btl_tcp_component_t { mca_btl_ba ...

  3. OpenMPI源码剖析4:rte.h 头文件的说明信息

    上一篇文章中说道,我们在 rte.h 中发现了有价值的说明: 我们一块一块来分析,首先看到第一块,关于 Process name Object: * (a) Process name objects ...

  4. OpenMPI源码剖析3:try_kill_peers 和 ompi_rte_abort 函数

    接着上一篇的疑问,我们说道,会执行 try_kill_peers 函数,它的函数定义在 ompi_mpi_abort.c 下: // 这里注释也说到了,主要是杀死在同一个communicator的进程 ...

  5. OpenMPI源码剖析2:ompi_mpi_errors_are_fatal_comm_handler函数

    上一篇文章说道,初始化失败会有一个函数调用: ompi_mpi_errors_are_fatal_comm_handler(NULL, NULL, message); 所以这里简单地进入了 ompi_ ...

  6. 老李推荐:第14章9节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-遍历控件树查找控件

    老李推荐:第14章9节<MonkeyRunner源码剖析> HierarchyViewer实现原理-遍历控件树查找控件   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员 ...

  7. 老李推荐:第14章5节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-查询ViewServer运行状态

    老李推荐:第14章5节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-查询ViewServer运行状态   poptest是国内唯一 ...

  8. 老李推荐:第14章6节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-装备ViewServer-启动ViewServer

    老李推荐:第14章6节<MonkeyRunner源码剖析> HierarchyViewer实现原理-装备ViewServer-启动ViewServer   poptest是国内唯一一家培养 ...

  9. 老李推荐:第14章3节《MonkeyRunner源码剖析》 HierarchyViewer实现原理-HierarchyViewer实例化

    老李推荐:第14章3节<MonkeyRunner源码剖析> HierarchyViewer实现原理-HierarchyViewer实例化 poptest是国内唯一一家培养测试开发工程师的培 ...

随机推荐

  1. 【转】Java学习---解析Java Servlet工作过程

    [原文]https://www.toutiao.com/i6594316694657696264/ 解析Java Servlet工作过程 Servlet简介 Servlet是sun公司提供的一门用于开 ...

  2. Coursera-AndrewNg(吴恩达)机器学习笔记——第四周编程作业(多分类与神经网络)

    多分类问题——识别手写体数字0-9 一.逻辑回归解决多分类问题 1.图片像素为20*20,X的属性数目为400,输出层神经元个数为10,分别代表1-10(把0映射为10). 通过以下代码先形式化展示数 ...

  3. 记录一次nginx配置vhost的小bug

    话说这篇博客是在是为了保持自己记录生活的习惯而写的,没有什么阅读的价值,各位读者可以直接忽略了.今天在配置一个域名的时候,写了new_example.com(举例而已) 因为是内测,所以并未想象到深层 ...

  4. fedora27安装后的配置工作(持续更新)

    换源 没什么可说的,安装后更换国内软件源是必须做的事,推荐更换阿里的镜像源.换源教程 添加epel源 EPEL (Extra Packages for Enterprise Linux)是基于Fedo ...

  5. from urllib.request import urlopen

    from urllib.request impor urlopen      (负责打开浏览url内的html 文本) re.compile(r'alex(?P<name>\d+)and' ...

  6. [pip] pip命令的安装、卸载、查找方法汇总

    比如以selenium的为例 1.打开命令窗口(如果是win10,最好是通过管理员方式打开命令窗口,否则会出现安装时提示访问不拒绝) 2.安装selenium的指定版本,命令:pip install ...

  7. Netty入门(八)构建Netty HTTP/HTTPS应用

    HTTP/HTTPS 是最常见的一种协议,这节主要是看一下 Netty 提供的 ChannelHaandler. 一.HTTP Decoder,Encoder 和 Codec HTTP 是请求-响应模 ...

  8. SQL必知必会摘要

    数据检索 2.2 检索单个列 SELECT prod_name FROM Products; SQL语句不区分大小写   2.3 检索多个列 SELECT prod_name,prod_id,prod ...

  9. Node.js实战(五)之必备JavaScript基础

    阅读本章的话,个人觉得之前使用过JavaScript,完全轻松. Node.js的核心类型有:number.boolean.string以及object.另外两种类型分别是函数合数组,其实它们你可以理 ...

  10. OpenCV——颜色缩减、计时函数、访问像素

    //颜色空间缩减 //src:源图片 //dst:目标图片 //divideWith:缩减宽度 void ColorSpaceReduction(Mat src,int divideWith,Mat& ...