【问题标题】:Why do we have lower and upper bounds for datatypes in MPI?为什么我们在 MPI 中有数据类型的下限和上限?
【发布时间】:2017-05-30 01:01:16
【问题描述】:

或者这个问题可以这样解释: 为什么需要一种下界非零的数据类型?

考虑以下示例:

struct S {
  int a;
  int b;
  float c;
  float d;
} array[N];

如果我有一个 S[] 类型的数组,并且我只想发送字段 bd,我将使用类型映射 { (4, MPI_INT), (12, MPI_FLOAT) } 创建一个 datatype。 起初,似乎这种类型可以用来正确发送一个数组 struct S:

MPI_Send(array, N, datatype, ...);

但是如果N > 1,这不起作用。 这样的类型将有lb = 4ub = 16extent = ub - lb = 12。那是, MPI 会认为数组的第二个元素从 12 个字节开始 第一个,这不是真的。

嗯,这可能没什么大不了的。毕竟,一般来说,对于这种部分发送的结构 我们必须指定结构的确切大小:

MPI_Type_create_resized(datatype, 0, sizeof(struct S), &resized);

但我想知道为什么我们总是需要指定零下限。为什么会 有人需要非零下限吗?下界非零的数据类型让我非常困惑,我无法理解它们。

如果我要为 MPI 设计一个类型系统,我会用一个单一的 参数 - 它的大小(范围),它是数组的两个相邻元素之间的步幅。在 MPI 方面,我总是设置lb = 0extent = ub。这样的系统在我看来更加清晰,并且在上述示例中可以正常工作。

但 MPI 选择了不同的方式。我们有两个 独立 参数:较低的 和上限。为什么会这样?这种额外的灵活性有什么用?什么时候应该使用具有非零下限的数据类型?

【问题讨论】:

    标签: mpi


    【解决方案1】:

    你不知道在科学和工程代码中发现了什么样的奇怪而复杂的结构。 The standard 旨在尽可能通用并提供最大的灵活性。第 4.1.6 节 下界和上界标记 开头如下:

    明确定义类型映射的下限和上限通常很方便,并覆盖第 105 页给出的定义。这允许定义在其开头或结尾具有“孔”的数据类型,或者具有超出上限或低于下限的条目的数据类型。第 4.1.14 节提供了这种用法的示例。 此外,用户可能希望覆盖 [sic] 用于计算上限和范围的对齐规则。例如,C 编译器可能允许用户为程序中的某些结构覆盖 [sic] 默认对齐规则。用户必须明确指定与这些结构匹配的数据类型的边界。

    具有非零下限的数据类型的最简单示例是具有用作偏移量的绝对地址的结构,例如,用于发送带有指向分散在内存中的数据的指针的结构。这种数据类型与指定为缓冲区地址的MPI_BOTTOM 一起使用,该地址对应于内存空间的底部(在大多数系统上为0)。如果下限固定为0,那么您必须找到具有最低地址拳头的数据项并计算相对于它的所有偏移量。

    另一个例子是使用MPI_Type_create_subarray 创建描述n 维数组的子数组的数据类型。如果下限为零,则必须提供指向子数组开头的指针。使用非零下限,您只需提供指向整个数组开头的指针。您还可以创建此类子数组数据类型的连续数据类型,以便从 n+1 维数组发送此类 n 维“切片”。

    【讨论】:

    • 对不起,但您的回答实际上并没有解释任何事情。 1) 我搜索了标准并找到了一些使用 MPI_Type_create_resized 的示例。但是,没有一个示例使用非零磅。 2) 我不明白你对 MPI_BOTTOM 部分的看法。您描述的结构本质上是一种结构。你永远不会有这样的结构数组。因此,只有类型映射对这种数据类型很重要,而边界根本不重要。 3) MPI_Type_create_subarray 创建一个 lb = 0 的数据类型。
    • 1) 标准中提供的示例并不详尽。具有负 LB 的类型的合法用途是处理具有负索引的 Fortran 数组,例如INTEGER A(-5:5)。 2) 类型的 LB 只是类型映射中的最小偏移量,除非使用 MPI_Type_create_resized 放置了显式的 LB 标记。 MPI 不仅仅用于发送数组。 3) 当子数组不是从数组的开头开始时,使用MPI_Type_create_subarray 创建的这个真正的数据类型 LB 是非零的。就像您在示例中所做的那样,它会放置明确的绑定标记。
    • 你没有在调用MPI_Type_create_resized时使用0作为LB。这样做是完全有效的:MPI_Type_get_extent(datatype, &lb, &extent); MPI_Type_create_resized(datatype, lb, sizeof(struct S), &resized);resized 将具有非零 LB,并且仍可用于发送 struct S 数组。为方便起见,使用0 的 LB 是因为它在大多数情况下在进一步组合数据类型时简化了计算偏移量。这就是子数组构造函数所做的,这就是为什么数据类型的 LB 显式设置为 0
    【解决方案2】:

    IMO 同时拥有 LB 和 UB 标记的唯一原因是它简化了数据类型构造的描述。 MPI 数据类型由类型映射(偏移和类型列表,包括可能的 LB/UB 标记)描述,所有数据类型构造调用根据旧类型映射定义新类型映射。

    当您在旧类型映射中有 LB/UB 标记并遵循从旧类型映射构建新类型映射的规则时,您会在新类型中获得 LB/UB 标记的自然定义,它定义了新类型映射的范围新型。如果范围是类型映射一侧的单独属性,则您必须为每个数据类型构造调用定义新范围。

    除此之外,我基本上同意你的观点,将 LB/UB 作为两个独立的数据片段是毫无意义的,而它们的唯一用途是定义范围。一旦添加了 LB/UB 标记,它们的含义就完全脱离了任何实际数据偏移量的概念。

    如果你想在位移 4 处放置一个 int 并且它的范围是 8,那么构造就可以了

    [(LB,4), (int,4), (UB,12)]
    

    但构造任何一个都一样好

    [(LB,0),(int,4),(UB,8)]
    [(LB,1000000),(int,4),(UB,1000008)]
    [(LB,-1000),(int,4),(UB,-992)]
    

    上述所有行为在行为上都是完全等价的,因为它们具有相同的程度。

    当对 LB/UB 标记的解释谈到您需要如何拥有第一个数据位移为非 0 的数据类型时,我认为这是一种误导。确实,您需要能够制作这样的类型,但 LB/UB 标记与数据位移没有根本的联系。我担心如果 MPI 用户认为 LB 与数据偏移量本质上相关,那么建议他们连接会导致他们编写无效代码。

    【讨论】:

      猜你喜欢
      • 2011-06-21
      • 2015-10-15
      • 1970-01-01
      • 2021-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多