或者问题可以这样解释: 为什么可能需要具有非零下限的数据类型?
考虑以下示例:
struct S {
int a;
int b;
float c;
float d;
} array[N];
如果我有一个S[]
类型的数组,并且我只想发送字段b
和
d
,我会创建一个datatype
类型地图{ (4, MPI_INT), (12, MPI_FLOAT) }
。
起初,似乎可以使用这种类型来正确发送数组
struct S
:
MPI_Send(array, N, datatype, ...);
但如果N > 1
,这不起作用。
这种类型会有lb = 4
,ub = 16
和extent = ub - lb = 12
。那是,
MPI会认为数组的第二个元素从中开始12个字节
第一个,这不是真的。
MPI_Type_create_resized(datatype, 0, sizeof(struct S), &resized);
但我想知道为什么我们总是需要指定零下限。为什么会 有人需要非零下限吗?具有非零下限的数据类型对我来说非常混乱,我无法理解它们。
如果我要为MPI设计一个类型系统,我会描述一个单一的类型
参数 - 其大小(范围),它是数组中两个相邻元素之间的步幅。就MPI而言,我总是设置lb = 0
和extent = ub
。这样的系统对我来说看起来更清楚,并且在上述示例中它可以正常工作。
但是MPI选择了不同的方式。我们有两个独立的参数:低级 和上限。为什么会这样?使用这种额外的灵活性是什么?应该何时使用具有非零下限的数据类型?
答案 0 :(得分:0)
您不知道在科学和工程规范中会发现什么样的奇怪和复杂的结构。 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 :(得分:0)
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 与数据偏移有内在关联,那么建议他们连接会导致他们编写无效代码。