存储多维可变长度数据的最有效(但又足够灵活)的方法是什么?

时间:2012-07-10 12:11:08

标签: c++ performance caching hpc fluid-dynamics

我想知道有效存储(以及随后访问)具有可变长度的多维数据阵列集的最佳实践。重点是性能,但我还需要能够在运行时更改单个数据集的长度而不需要太多开销。

注意:我知道这是一个有点冗长的问题,但我已经四处寻找了很多,并且无法找到解决方案或示例来描述手头的问题并且准确无误。

背景

上下文是基于不连续Galerkin谱元素方法(DGSEM)的计算流体动力学(CFD)代码(参见Kopriva(2009),实现偏微分方程的光谱方法) 。为简单起见,让我们假设一个2D数据布局(实际上它是三维的,但从2D到3D的扩展应该是直截了当的。)

我的网格由K方形元素kk = 0,...,K-1)组成,可以是不同的(物理)大小。在每个网格元素(或“单元格”)k中,我有N_k^2个数据点。 N_k是每个维度中的数据点数,可以在不同的网格单元格之间变化。

在每个数据点n_k,i(其中i = 0,...,N_k^2-1)我必须存储一组解决方案值,它们在整个域中(即无处不在)具有相同的长度nVars,并且在运行期间不会改变。

尺寸和变化

网格单元格K的数量为O(10^5)O(10^6)可以在运行时更改。
每个网格单元格中的数据点N_k的数量在28之间,可以在运行时期间发生变化(对于不同的单元格可能会有所不同)。<登记/> 存储在每个网格点的变量nVars的数量大约为510,而在运行时不能更改(对于每个网格单元也是如此)

要求

性能是这里的关键问题。我需要能够以有效的方式在所有单元的所有网格点上以有序的方式定期迭代(即,没有太多的高速缓存未命中)。通常,KN_k在模拟过程中不会经常发生变化,因此例如可以选择所有单元格和数据点的大量连续内存块。

但是,我确实需要能够在运行时期间细化或粗化网格(即删除单元格并创建新单元格,新的单元格可以附加到末尾)。我还需要能够更改近似顺序N_k,因此我为每个单元格存储的数据点数量也可以在运行时更改。

结论

感谢任何输入。如果您有自己的经验,或者只是了解一些我可以看到的好资源,请告诉我。然而,虽然解决方案对最终程序的性能至关重要,但它只是众多问题中的一个,因此解决方案需要具有应用性,而不是纯粹的学术性。

如果这是一个提出这个问题的错误场所,请告诉我一个更合适的地方。

2 个答案:

答案 0 :(得分:3)

通常,这些类型的动态网格结构可能非常难以有效地处理,但是在块结构的自适应网格细化代码中(常见于天体物理学中,复杂的几何形状并不重要)或者您的光谱元素代码大块大小,通常不是问题。你需要做很多工作来完成每个块/元素(在你的情况下至少有10 ^ 5个单元x 2个单元/单元),块之间切换的成本相对较小。

请记住,在大量的块数据已经存在于缓存中之前,通常不能对每个元素或块执行太多的艰苦工作。无论如何,在N + 1块上完成大量工作之前,你已经不得不将大部分块N的数据从缓存中刷新。 (在您的代码中可能有一些操作是例外,但这些操作可能不是您花费很多时间,缓存或没有缓存的操作,因为没有大量的数据重用 - 例如,元素操作细胞价值)。因此,将每个块/元素保持在彼此旁边不一定是一个大问题;另一方面,你肯定希望块/元素本身是连续的。

另外请注意,您可以移动块以保持连续,因为调整大小,但不仅所有这些内存副本也将擦除缓存,但内存副本本身变得非常昂贵。如果你的问题填满了很大一部分内存(并不是我们总是吗?),比如说1GB,你需要在完善之后将其中的20%左右移动以使事情再次连续,那就是.2 GB(读+写) )/ ~20 GB / s~20 ms与重新加载(比如说)16k高速缓存行相比,每个~100ns~1.5 ms。无论如何,你的缓存在shuffle之后被破坏了。如果您知道自己很少进行细化/细化,那么这可能仍然值得做。

但是作为一个实际问题,天体物理流体动力学中的大多数自适应网格代码(我知道代码足够好)可以简单地维护一个块及其元数据的列表,而不用担心它们的邻接性。 YMMV当然。我的建议是 - 在花费太多时间制作完美的数据结构之前 - 首先只测试两个元素的操作,两次;第一个,按顺序排列元素并对它们进行计算1-2,第二个按“错误”顺序进行操作2-1,并对两次计算进行多次计时。

答案 1 :(得分:1)

对于每个单元格,存储在连续数组中查找单元格数据的偏移量。这种偏移映射非常有效并且被广泛使用。您可以重新排序单元格以在遍历中重用缓存。当单元格的顺序或数量发生变化时,创建一个新数组并进行插值,然后丢弃旧数组。这种存储对于外部分析来说要好得多,因为可以在不参考网格的情况下管理Krylov方法中的内部产品和Runge-Kutta方法中的阶段等操作。它还需要每个载体的最小记忆(例如在Krylov碱基中并且具有时间积分)。