编译器(Visual C++)如何优化按索引访问矢量元素?

How compiler (Visual C++) optimize accessing vector elements by index?

本文关键字:访问 索引 元素 C++ Visual 何优化 编译器 优化      更新时间:2023-10-16

>我正在尝试构建树,其中每个节点可以有无限的子节点,并从根向下传播一些值。我尝试了多种方法来实现这一目标。 首先是通过在堆上分配每个单独的节点。然后我尝试创建节点向量(不是指向节点的指针(,并使用指向该向量元素的节点指针构建树。 这是更快的解决方案,因为我猜我将节点存储在一个内存块中。这个解决方案效果很好,但如果矢量调整大小,我必须再次重建整个树。第三种选择是使用索引而不是指针来使用构建树。只有索引失效的时间是当我从矢量中删除元素时,但它只是一个节点,所以没什么大不了的(将最后一个元素与删除的元素交换(。这个解决方案是我机器上最慢的解决方案,在调试模式下(使用 Visual Studio 2019(,其他两个解决方案在调试模式下表现得更好。但是当我切换到具有优化的发布模式时,此解决方案的性能类似于我使用指向矢量元素的指针的解决方案。我读到当优化打开时,编译器可能会直接使用指针而不是索引。但我真的无法回答这个问题。所以我的问题是到底发生了什么?我是否可以假设使用索引的代码速度几乎始终相同?

使用指针的节点:

struct Node 
{
void SetParent(Node* parent)
{
Parent = parent;
NextSibling = parent->FirstChild;
parent->FirstChild = this;
}
void Propagate(float x,float y)
{ 
Node* tmp = this;
while (tmp)
{
tmp->X += x;
tmp->Y += y;
if (tmp->FirstChild)
tmp->FirstChild->Propagate(tmp->X,tmp->Y);
tmp = tmp->NextSibling;
}
}
float X, Y;
std::string Name;
Node* Parent = nullptr;
Node* FirstChild = nullptr;
Node* NextSibling = nullptr;
}

使用索引的节点:

struct Node
{
void SetParent(std::vector<Node>& list,int parent)
{
Parent = parent;
NextSibling = list[parent].FirstChild;
list[parent].FirstChild = Index;
}
void Propagate(std::vector<Node>& list, int index, float x, float y)
{
while (index != -1)
{
list[index].X += x;
list[index].Y += y;
if (list[index].FirstChild != -1)
{
list[list[index].FirstChild].Propagate(list, list[index].FirstChild, list[index].X, 
list[index].Y);
}
index = list[index].NextSibling;
}
}

float X, Y;
std::string Name;
int Index = -1;
int Parent = -1;
int FirstChild = -1;
int NextSibling = -1;
};

我们在评论中被告知,比较调试版本的性能并不是很有用。该声明是正确的,这里的问题不是发布版本做了一些神奇的事情来访问您的向量,而是调试版本因大量未进入发布版本的验证和检测代码而减慢

。考虑: 根据 stdc++ 实现,T operator[](size_t index) const可以在调试版本中包含范围检查代码,当您直接使用指针(IIRC,即 Visual Studio stdc++ 实现中的工作方式(时,您只需绕过这些代码即可。

当然,使用指针访问的调试构建会更快,但您已经发现了许多陷阱。

总结总结:在您的情况下使用索引是完全可以的。它们不会损害性能。在此类情况下,通常不是索引受到伤害,而是这些内存访问的连贯性,您是否破坏了 CPU 缓存等。