我面临的问题是分割大型数据集(最多2048x2048x40x10000 x,y,z,t ==几个TB解压缩,给予或接受)。从好的方面来说,这个数据集中的特征相当小;最大20x20x20x20。
据我所知,没有开箱即用的解决方案(纠正我,如果我错了)。我有一些如何解决这个问题的计划,但我希望得到您的反馈。
一次最大约600mb;在典型情况下较少;我可以在我的4gb内存中保存一堆相应的切片。
鉴于我的功能很小,我的直觉说最好避开所有形式的分段聪明,并简单地对我的标签进行局部迭代泛滥填充更新;如果你的邻居有更高的标签,请复制它;迭代直到收敛。迭代次数应该受到任何维度中最大簇大小的限制,同样应该很小。
CUDA对3D有着天生的偏好,所以我可以分两步进行;迭代所有尚未收敛的3d体积切片。然后简单地在所有连续时间片上执行元素循环,并执行相同的floodfill逻辑。
我可以使用简单的递增唯一计数器初始化迭代,或首先找到局部最大值,然后在那里生成种子标签。后者是首选,所以我可以保持一个由标签索引的数组来存储所有区域的x,y,z,t最小/最大范围(也可以像后处理一样)。如果某个区域没有扩展到最新的时间片,则会从数据中删除该区域,并将其位置写入数据库。如果尾随时间片已完全以这种方式被挖出(总和为零),则将其从内存中删除。 (或者如果内存溢出,也可以删除最新的内容;这样做的近似值必须用尽)
这似乎应该有效。考虑到z维度的有限大小,您认为启动x,y,z线程块或启动x,y块并让每个线程在z维度上循环更好吗?这是一种“尝试和看到”的事情,还是有股票回答呢?
刚刚发生的另一项优化;如果我将一个x,y,z块加载到共享内存中,那么执行几次Floodfill更新会不会更快,而无论如何我都有内存?也许它最好让本地内存迭代收敛,然后继续......它与我想的上述问题有关。单个邻居 - 最大查找可能是一个次优的计算强度,因此无论是在z上循环还是迭代几次都应该抵消那个。我想我更喜欢后者。
另一个问题;看起来这样的事情似乎不存在,但是包含模板代码的项目链接会做类似的事情会受到高度赞赏(优化的3d Floodfill代码吗?),因为我的CUDA知识仍然不稳定。
提前感谢您的想法和反馈!
答案 0 :(得分:1)
记录;我有一个令人满意的工作解决方案;当它稍微成熟时,可能会把它放在某处。这是一个描述:
我现在所做的是:
这给出了一个分水岭指针图像,每个指针指向一个唯一的标签。
然后我做了一个合并的步骤;如果相邻像素指向不同的标签,并且它们的值超过阈值,我合并它们的标签,以便“充分连接”的最大值不会形成它们自己的区域。
然后我取消引用指针,我有一个很好的标签图像。 由于我仅在最大值上播种标签,因此最大标签数量较少;我分配了一个6xN大小的数组,并使用原子来计算每个特征的最小/最大范围。
目前没有使用任何共享内存,但它已经非常快。我意外地做了半聪明的事情;因为我首先做了一个分水岭,然后使用单次扫描合并相邻的水流区域,我实际上正在进行各种级别的多级算法。这一系列的洪水填充调用以及两次或三次合并扫描主导了性能。正确使用共享内存可以将我的内存带宽需求减少5倍,但是等待直到它明确表现为瓶颈。我已经得到了一个更像我想要的分段,而不是我可以从scipy中解决的分段,它更快,并且使用一个数量级更少的内存,所以我现在很高兴。
答案 1 :(得分:0)
最简单的方法是使用1D存储并在其上覆盖4D索引架构。
address = x + y * width + z * height * width + t * length * height * width;
这意味着
数据(0,0,0,0)和数据(1,0,0,0)是连续的地址,但是
data(0,0,0,0)和data(0,0,0,1)是width * height * length address part。
但是如果您的访问模式正在处理最近邻居,那么您需要空间局部性来进行索引。
这可以使用Morton Z(Peano Key)排序索引来实现。它将最近的邻居放在紧密的线性内存地址中。该Z阶线性地址是通过交织x,y,z,t索引的交替比特获得的。有关2D示例,请参阅
http://graphics.stanford.edu/~seander/bithacks.html#InterleaveTableObvious
答案 2 :(得分:0)
我建议使用金字塔形处理方案:您可以快速计算类似mipmap的细节级别,其中存储组合体素的最大/最小值。这应该会产生类似于八叉树的东西。然后,您可以仅在有趣的区域开始分割,这可以提供相当大的加速。如果你只有很少甚至很小的段,这应该可以很好地工作。
我猜你也可以使用关卡集(可以在GPU上实现),但我会重新调整这些实现(谷歌的“CUDA级别设置”)。
您是否需要随时跟踪您的细分?你知道怎么做吗?