HDF5 中的嵌套复合数据类型

Posted

技术标签:

【中文标题】HDF5 中的嵌套复合数据类型【英文标题】:Nested compound datatypes in HDF5 【发布时间】:2013-12-04 14:25:30 【问题描述】:

我是一个 C++ 应用程序团队的一员,该应用程序处理各种类型的消息并以各种格式输出它们。出于讨论的目的,可以将消息视为名称-值对的集合。这些值通常是数字,但也可以是字符串。消息的结构基本上是在处理时被发现的。 消息可以任意大,因此不允许在内存中存储表示。消息一次处理一个名称-值对。 消息可以具有内部结构,该结构由名称-值对中的名称捕获。一个很好的类比是考虑目录层次结构中的文件名。

我正在开发一个处理这些消息并使用低级 HDF5 API 生成 HDF 输出的子系统。由于我上面描述的限制,我使用的方法涉及两次传递消息。在第一遍中,我收集布局信息并构建复合数据类型和数据集。然后我再次传递消息以写出值。因为我一次写入一个值,所以我有一个这样的序列:


 // name, value, dataType, dtSize, ctDataSet and ctSpace have been defined elsewhere 
hid_t valueDT = H5Tcreate(H5T_COMPOUND, dtSize);
herr_t status = H5Tinsert(valueDT, name, 0, dataType);
hid_t filespace = H5Dget_space(ctDataSet);
hsize_t offset[] =  0 ;
hsize_t dim[] = [ 1 ;
status = H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, NULL,
                                             dim, NULL);
status = H5Dwrite(ctDataSet, valueDT, ctSpace, filespace, H5P_DEFAULT, &value);

我已经完成了这项工作,现在我正在尝试扩展它以处理嵌套的复合数据类型。我已经完成了第一次传球,但我被困在第二次传球上。 sn-p 中的代码构建一个与值关联的独立数据类型,为其命名与数据集中已存在的字段对应的名称,然后哄骗 HDF5 将值作为数据集的一部分写出。 我意识到我没有明确说明所使用的名称。假设我们正在查看 position 中的字段 x。使用的名称将是 position.x

当值属于内部复合数据类型时,我对如何建立这种关联感到困惑。任何见解都将不胜感激。

【问题讨论】:

【参考方案1】:

您需要在每个嵌套级别创建正确的 HDF5 数据类型,从底部开始。然后,一旦您可以使用总复合数据类型创建数据集并继续将数据写入文件。

我刚刚完成了类似的操作,只是我在 Common Lisp 中使用了 HDF5。我最终做的是定义一个类型规范(听起来像你的消息)来完整描述数据类型的结构,然后使用 memoization 生成 C 结构(用于记住函数输出的花哨的词)。

举个例子:假设你有以下结构(伪代码):

Type = 
    x of Xtype;
    y of Ytype;

您可以通过以下方式为 Type 创建复合数据类型

    为 Xtype 创建 HDF5 数据类型(这是递归的,应该记住) 为 Ytype 创建 HDF5 数据类型(就像这样) 使用 Xtype 和 Ytype 的 HDF5 数据类型为 Type 创建 HDF5 数据类型。

这确实要求您在第一次通过时已经完全确定了数据类型的结构,但这听起来与您的操作方式无关。

要扩展示例,您需要做的是

    获取复合结构的数据类型规范列表。 (递归)为每个类型规范生成一个 HDF5 数据类型 从生成的 HDF5 数据类型组装总的 HDF5 复合类型。

将数据填充到(递归)复合结构中

通常有两种方法。

源代码生成

您可以在第一次传递数据期间创建用于访问/填充结构的代码。如果您想编写了解 C/C++ 结构和类的代码,这是必要的,因为数据类型是静态的,所以如果一个类型在编译时不存在,那么它在运行时也不存在。因此,您可以通过生成 C++ 代码使该类型在编译时存在,然后编译并作为第二遍运行。

对于您正在做的事情,这种方法听起来不太有希望,因为听起来您将处理相当大的消息流,这需要大量的代码生成和编译。所以,继续下一个方法:

原始二进制数据访问

这种方法完全取消了对结构使用静态类型,从而消除了生成 C/C++ 复合类型的要求。

您所做的是使用您所拥有的有关数据类型的信息来计算复合数据类型的总大小以及类型成员应该出现在总类型的内存块中的位置的偏移量。这可以像 HDF5 类型生成一样递归完成。

例子:如果你有复合类型

Type = 
    x of Xtype;
    y of Ytype;

你会

    通过递归下降到Type的结构得到Type的总大小,将XtypeYtype的大小相加,以同样的方式下降到它们的结构中。

    在内存中分配Type字节的大小。

    获取构成Type 对象的所有基本结构元素的偏移量。所以如果Xtype是复合的,那么你必须得到Xtype的成员,如果其中任何一个是复合的,你就得到它的成员,等等。

    将每个基本结构元素写入分配的内存中的适当偏移量。这也必须递归完成,因为 XtypeYtype 可能是复合类型。

这种方法之所以有效,是因为数据是连续分配的(至少从程序员的角度来看),因此xy 并排放置在分配的内存中。

在这种方法中,您不得不放弃使用结构/类的便利性,但这是编译器在幕后管理结构/类的方式。

【讨论】:

这听起来很有希望,但是在您所描述的内容中有些东西让我无法理解。具体来说,假设在我的消息中,有一个名为“position”的复合类型,其中包含名为“x”、“y”和“z”的元素。我在第一遍中构建了整体复合数据类型。我在第二遍,我即将写出 position/x 的值。我要使用的数据类型是我在您的阶段 2.2 中合成的“位置”数据类型吗?数据类型如何与数据集中的孪生关联? 除非我误解了你,否则你提出的方法要求将所有消息值插入内存数据集,然后将其写出。我试图在第二遍中处理每个值时立即写出它。我包含的代码 sn-p 显示了我得到的安排(在 hdf-forum 上的好人的大力帮助下)。我已经编辑了原始问题以强调一些更麻烦的约束...... 啊,废话,我明白你的意思了。不过,我想我为您指明了正确的方向:查看 H5TB 高级接口函数 H5TBwrite_fields_name H5TB.c 的源代码。这演示了如何在复合数据类型中只设置一个字段,我猜这也可以递归地对成员数据类型完成。这可能是您在未显示的代码中已经在做的事情;如果是这样,那么我所能建议的就是尝试在您需要访问的每个字段上递归地使用 H5Tinsert 函数。 再想一想:您真的想要/需要为此使用 HDF5 吗? HDF5 针对大量相似数据进行了优化,例如具有固定数量字段的 3-d 位置或表格。您正在做的事情听起来更像是一个树形结构,其中每条消息都可以具有与任何其他消息不同的任意结构。我认为也许某种二进制 XML 比 HDF5 更适合这个? HDF 实际上非常适合我们正在尝试做的事情。尽管消息彼此不同,但流中有许多给定消息类型的实例。我现有的实现按原样处理大量消息类型 -​​ 嵌套复合数据类型的处理是最后的障碍之一。【参考方案2】:

我最终尝试了一种与我最初描述的方法完全不同的方法。我没有尝试将消息映射到复合数据类型,而是创建了一个***,并为消息中的每个字段创建了一个数据集。在处理消息的后续实例时,我会更改每个 数据集 的范围。在消息字段是可选的情况下,我在 数据集 上设置一个属性来识别哪些消息实例具有与之关联的值。

令我有些惊讶的是,在相同输入的情况下,“多数据集”方法会产生比“复合数据集”方法更大的输出文件。我天真的期望它会更小 - 但如果使用我错过的 API 有一些微妙之处,我不会感到惊讶。然而,还有一个更令人烦恼的问题。输出文件的使用者将使用 MATLAB 来读取它们。 MATLAB 确实有一些高级函数来读取 HDF5 文件 - 特别是 h5read()。由于实现了 h5read(),其参数之一是您要提取的 数据集 的名称。我真的不想要求用户单独提取他们可能感兴趣的每个数据集。谁能想到将*** group 包装在 dataset 中的合理方法?

【讨论】:

以上是关于HDF5 中的嵌套复合数据类型的主要内容,如果未能解决你的问题,请参考以下文章

HDF5:复合数据类型,用于写入包含指向另一个结构的指针的结构

ElasticSearch入门 第六篇:复合数据类型——数组,对象和嵌套

Mathematica HDF5 和复合阵列

是否可以从 Python 的 HDF5 文件中的复合数据集中读取字段名称?

通过MATLAB将矩阵数据写入HDF5文件中的每个数据类型成员

ElasticSearch实战-复合数据类型