繁体   English   中英

使用向量在C ++中声明3D数组结构

[英]Declaring 3D array structure in c++ using vector

嗨,我是一名研究生,学习使用c ++进行科学计算。 我们的一些研究集中在算法的速度上,因此构造足够快的数组结构非常重要。

我已经看到了构造3D阵列的两种方法。 第一个是使用向量库。

vector<vector<vector<double>>> a (isize,vector<double>(jsize,vector<double>(ksize,0)))

这给出了大小为isize x jsize x ksize的3D数组结构。

另一个是使用以下命令构造一个包含大小为isize * jsize * ksize的1d数组的结构

new double[isize*jsize*ksize] 为了轻松访问(i,j,k)的特定位置,必须重载运算符(对吗?)。

根据我的经验,第一个要快得多,因为它可以轻松访问位置(i,j,k),而后一个则必须计算位置并返回值。 但是我看到有些人更喜欢后一个而不是第一个。 他们为什么偏爱后者? 使用第一个有什么缺点吗?

非常感谢。

两者之间的主要区别是布局:

vector<vector<vector<T>>>

这将为您提供vector<vector<T>>一维数组。
每个项目都是vector<T>一维数组。
这些一维数组的每一项将是T的一维数组。

关键是, vector本身不存储其内容。 它管理一块内存,并将内容存储在那里。 这会带来许多不良后果:

  1. 对于尺寸为X·Y·Z的矩阵,最终将分配1 + X + X·Y内存块。 那太慢了,会浪费掉堆。 想象一下:大小为20的多维数据集矩阵将触发421个对new调用!
  2. 要访问一个单元,您有3个间接级别:
    • 您必须访问vector<vector<vector<T>>>对象以获取指向顶级内存块的指针。
    • 然后,您必须访问vector<vector<T>>对象以获取指向第二级内存块的指针。
    • 然后,您必须访问vector<T>对象以获取指向叶内存块的指针。
    • 只有这样,您才能访问T数据。
  3. 这些内存块将散布在堆周围,从而导致大量的高速缓存未命中并减慢了整体计算的速度。
  4. 如果您在某个时候弄错了,则可能会以矩阵中的某些行的长度不同而结束。 毕竟,它们是独立的一维数组。

另一方面,拥有一个连续的内存块(如new T[X * Y * Z] )将得出:

  1. 您分配1个内存块。 没有堆垃圾,O(1)。
  2. 您只需要访问指向内存块的指针,然后就可以直接查找所需的元素。
  3. 所有矩阵在内存中都是连续的,这是缓存友好的。

那些日子里,单个缓存未命中意味着数十或数百个计算周期的损失,请不要低估缓存友好性。

顺便说一句,您可能没有提到更好的方法:使用众多矩阵库中的一种,该库将自动为您处理并提供出色的支持工具(例如SSE加速矩阵操作)。 Eigen是这样的图书馆之一,但还有很多其他图书馆。

→您想进行科学计算吗? 让lib处理样板和基础知识,以便您可以专注于科学计算部分。

在我看来, std::vector与普通的普通数组相比有太多优势。

简而言之,这里是一些:

  • 使用std::vector创建内存泄漏要困难得多。 仅此一点是最大的优势之一。 这与性能无关,但应始终考虑。
  • std::vector是STL的一部分。 C ++的这一部分是最常用的部分之一。 成千上万的人使用STL,因此他们每天都会受到“测试”。 在过去的几年中,他们进行了如此彻底的优化,因此不再缺乏任何性能。 (请纠正我,如果我看到这个错误)
  • std::vector处理很容易成为1,2,3。没有指针就什么都不做...只需通过方法或使用[] -operator和更多其他方法来访问它。

首先,直接在vec ^ 3中访问(i,j,k)的想法有些缺陷。 您所拥有的是一个指针结构,在此过程中需要取消对三个指针的引用。 请注意,我不知道这是比计算一维数组中的位置快还是慢。 您需要进行测试,并且这可能取决于数据的大小(尤其是是否适合大块数据)。

其次,向量^ 3需要指针和向量大小,这需要更多的内存。 在很多情况下,这将是无关紧要的(因为图像呈三次方增长,但内存差异仅二次方增长),但是如果您的算法确实要填充任何可用的内存,那可能很重要。

第三,原始数组将所有内容存储在连续的内存中,这有利于流传输,并且由于快速的高速缓存访​​问而对于某些算法也很有利。 例如,当您将一个3D图像添加到另一个时。

请注意,所有这些都与您可能不需要的超优化有关。 skratchi.at在他的答案中指出的矢量的优点非常强大,我还补充了矢量通常可以提高可读性的优点。 如果没有充分的理由不使用向量,请使用它们。

无论如何,如果您决定使用原始数组,请确保将其包装好并保持类的大小和简单性,以解决有关泄漏等问题。

欢迎来到SO。

如果您拥有的一切都是两种选择,那么第一种可能会更好。

优先使用STL数组或向量而不是C数组

您应该避免使用C ++普通数组,因为您需要使用new/delete和其他样板代码(如跟踪大小/检查范围)来管理自己的内存分配/取消分配。 用明确的话说: “ C数组不太安全,并且与数组和向量相比没有任何优势。”

但是,在第一种选择中存在一些重要的缺点。 我想强调的是:

std::vector<std::vector<std::vector<T>>>

不是3维矩阵。 在矩阵中,所有行的大小必须相同。 另一方面,在“向量的向量”中,不能保证所有嵌套的向量都具有相同的长度。 原因是向量是@spectras答案中指出的线性一维结构。 因此,为避免各种不良行为或意外行为,您必须在代码中包括防护措施,以确保得到保证的矩形不变式。

幸运的是,第一种选择不是您手中可能只有的一种。

例如,您可以将c样式数组替换为std :: array:

const int n = i_size * j_size * k_size;

std::array<int, n> myFlattenMatrix;

或使用std::vector来改变矩阵尺寸。

通过其3个坐标访问元素

关于你的问题

为了轻松访问(i,j,k)的特定位置,必须重载运算符(对吗?)。

不完全是。 由于std :: vector和array都没有3参数运算符,因此您不能重载它。 但是您可以创建一个模板类或函数来为您包装它。 无论如何,您都必须参考这三个向量或计算线性存储中元素的展平索引。

考虑不要在实验中使用像Eigen这样的第三方矩阵库

您不是将其编码用于生产,而是用于研究目的。 特别是,您的研究完全是关于算法的性能。 在那种情况下,我不建议绝对不要使用像Eigen这样的第三方库。 当然,这取决于您愿意收集什么样的“算法速度”指标,但是例如Eigen会做很多事情(例如矢量化 ),这会对您产生巨大影响实验。 由于很难控制这些看不见的优化,因此这些库的功能可能会导致您得出关于算法的错误结论。

算法的性能和big-o表示法

通常,算法的性能是使用big-O方法进行分析的,其中不考虑实际花费的时间,硬件速度或编程语言特性等因素。 Adam Drozdek的书“ C ++中的数据结构和算法”可以提供有关它的更多详细信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM