从 2D 矢量切换到 1D 矢量

Posted

技术标签:

【中文标题】从 2D 矢量切换到 1D 矢量【英文标题】:Switching from a 2D vector into a 1D vector 【发布时间】:2022-01-22 01:11:19 【问题描述】:

我听说向量的向量在性能方面很糟糕。比如我有下面的二维std::vector

std::vector< std::vector<char> > characterMatrix;

// for iterating
for ( int row = 0; row < getY_AxisLen( ); ++row )

    for ( int column = 0; column < getX_AxisLen( ); ++column )
    
        std::cout << characterMatrix[ row ][ column ];
    

在这种方法中,矩阵会在 3-12 毫秒内打印在我的系统上。如果我看到例如减少,我会很高兴。 1-3 毫秒。

据我所知,每个内部向量(即 rows)都存储在堆内存的不同位置。所以这会导致很多碎片。 不仅如此,我的编译器中的 sizeof(std::vector) 返回 24(字节)。所以这意味着,例如,如果characterMatrix 有 50 行(也称为内部向量),那么它将在堆上分配 24*50 == 1200 字节来存储这 50 个向量的控制块这是在矩阵中实际数据 (chars) 占用的空间之外的。

现在,如果我想将所有 chars 保存在一个连续的内存块中,也许我可以将其写为一维向量,例如:

std::vector< char > characterMatrix;

// for iterating
for ( int row = 0; row < getY_AxisLen( ); ++row )

    for ( int column = 0; column < getX_AxisLen( ); ++column )
    
        std::cout << characterMatrix[ row * getX_AxisLen( ) + column ]; // is this correct?
    

这是一种有效的方法吗?如果我想以这种方式更改矩阵变量的实现,有人能告诉我应该记住什么吗?可能的缺点是什么?

【问题讨论】:

我“听说”在做出决定之前应该测量、分析和基准测试(优化的构建)。 nitpick:内部向量作为外部元素连续存储,但您的推理是正确的,因为内部向量将元素存储在堆上,因此矩阵的元素不连续 性能取决于很多因素,并且应该始终进行衡量!优化通常以可读性/可维护性为代价,因此就像工程一样,它是一种权衡。但是是的,向量的向量可能会导致数据在内存中更加分散,然后是连续分配,因此您不会充分利用缓存(预测)。也许你听说过 if 也很糟糕(分支预测)......那么你的实际问题是什么? 视情况而定。例如,如果您经常调整行大小,那么 std::vector&lt;std::vector&lt;char&gt;&gt; 甚至可能比 std::vector&lt;char&gt; 执行得更好。对于稀疏矩阵,您可以考虑使用std::map&lt;index,int&gt;。正如其他人所说,唯一知道的方法就是测量 @digito_evo:只是我在其他问题上看到的大多数 cmets 都类似于 “将一维向量用于矩阵”,即使对于初学者来说也没有其他考虑 :-(。在这里你得到了“先测量”“它是瓶颈吗?”“它更具可读性”?这让我很开心。跨度> 【参考方案1】:

“听说过”与表演相结合从来都不是正确的方法。 要解决性能问题,黄金法则是:基准第一!

此外,性能并不总是最重要的。通常,只有在发现应用程序的速度不足以满足您的目的时,您才应该优化性能。然后,按照以下步骤操作:

首先,您需要确保您正在查看的代码部分实际上是应用程序中的性能瓶颈(为此使用分析器)。无需优化总共只占用 1% 计算时间的东西——即使将其加速 10 倍,也只会将整体执行时间减少 0.9%!如果您发现这种 2D 矢量访问确实是瓶颈,那么基准测试它为未来的实验提供基准。 其次,您的代码需要正确。如果在优化性能后它没有做正确的事情,那么它的速度是没有用的。在这里,我建议进行测试(即,使用幼稚/未优化的方法记录结果,以便在优化时知道是否仍然得到相同的结果)。 第三,性能优化往往会对代码的可读性和/或可维护性产生负面影响。但是,这些可能非常重要,尤其是对于那些需要很好理解或变化很大的代码部分。

是的,二维向量表明您的性能可能不理想,因为正如您所说,数据并非全部在一个地方。 话又说回来,正如您目前所做的那样,在最内层循环内的每次访问中完成的索引计算也是“昂贵的”。 因此,将数据放在一个大矢量而不是二维矢量场中会更快;特别是如果您总是必须处理所有元素并且不需要“邻居”访问,这意味着您可以简单地让一个循环从 0 迭代到您所谓的 getY_AxisLen() * getX_AxisLen()。如上所述,建立一个基准可以帮助您找出哪些优化是有意义的! 为了解决可读性/可维护性降低的问题,在您的情况下,抽象出用于存储 2D 数据的实际数据结构可能会有所帮助,以便从访问数据的地方隐藏数据存储方式的实际实现。

【讨论】:

在命令提示符中打印出几千个chars 时,for 循环需要 3 到 12 毫秒。我对性能非常满意,因为我有一个非常旧的 CPU。但我仍然想让它对缓存更友好。 我很确定,在执行打印内容的循环时,大部分时间实际上都花在了打印本身上,而不是迭代或值检索...跨度> 对不起,我的意思是毫秒! 我会把正确性放在首位。可读性对此有所帮助,如果做错事,最快的代码就不好了 为了可读性,我不会使用向量向量。我只会对我的数据使用适当的抽象,隐藏实现细节。

以上是关于从 2D 矢量切换到 1D 矢量的主要内容,如果未能解决你的问题,请参考以下文章

在 cpp 中使用 2d 矢量

谷歌地图下载器可以下载矢量路网吗?

如何删除 2D 矢量网格中的部分路径?

Android Dark 主题反转矢量可绘制对象的颜色

将数据从数组切换到向量

使用纵横比旋转2D矢量