按值传递对象时与按引用传递对象时运行时较慢
Slow runtime when objects are passed by value vs when passed by reference
这是一个名为 Graph
的类的构造函数。在构造函数中,我正在尝试初始化某些内容,这是有效的构造器,即它运行并完成:
Graph(const float density, const int numVertex = 50): numEdges(0) {
graph.resize(numVertex, vector<Vertex>(numVertex));
for (int s = 0; s < numVertex; ++s) {
for (int k = 0; k < s; ++k )
if (edge_exist(density)) {
graph[s][k].visited = graph[k][s].visited = false;
graph[s][k].distance = graph[k][s].distance = _MAX;
++numEdges;
int distance = rand() % 10 + 1;
graph[s][k].edges.emplace_back(piiv(distance, graph[k][s]));
graph[k][s].edges.emplace_back(piiv(distance, graph[s][k]));
}
}
}
typedef pair<int, Vertex> piiv;
vector<vector<Vertex> > graph;
该类有一个名为graph
的字段,它是名为Vertex
的结构向量的向量。
typedef struct vert {
std::list<std::pair<int, vert> > edges;
int distance;
bool visited;
} Vertex;
现在,如果我保持顶点结构原样,而是使向量只是一个一维向量
vector<Vertex> graph;
并将构造函数更改为如下所示:
Graph(const float density, const int numVertex = 50): numEdges(0) {
graph.resize(numVertex);
for (int s = 0; s < numVertex; ++s) {
for (int k = 0; k < s; ++k )
if (edge_exist(density)) {
graph[s].visited = graph[k].visited = false;
graph[s].distance = graph[k].distance = _MAX;
++numEdges;
int distance = rand() % 10 + 1;
graph[s].edges.emplace_back(piiv(distance, graph[k]));
graph[k].edges.emplace_back(piiv(distance, graph[s]));
}
}
}
现在,此更改导致代码的运行时间比向量为 2 维时的通常运行时间长得多。我没有耐心弄清楚它运行了多长时间,但我知道它运行得比第一个慢得不合理,而且没有明显的原因。好吧,对我来说并不明显,但我敢打赌这里有人对为什么会这样有一些见解。
所以我的问题是,是什么导致了程序中这种看似不必要的延迟?如果有帮助,这就是构造函数的调用方式:
Graph G(0.4);
我已经跟踪了问题来自第二个构造函数实现中的最后 2 行:
graph[s].edges.emplace_back(piiv(distance, graph[k]));
graph[k].edges.emplace_back(piiv(distance, graph[s]));
所以我想真正的问题是上述内容与第一个构造函数中所做的有什么不同?
编辑
宾果游戏!在调试时,我决定更改要声明的顶点结构,如下所示:
typedef struct vert {
std::list<std::pair<int, vert&> > edges;
int distance;
bool visited;
} Vertex;
这似乎解决了问题,但为什么??为什么与按引用传递相比,按值传入顶点对象不起作用?
@Joe Z很好地总结了发生这种情况的原因。
std::list<std::pair<int, vert> > edges;
通过将Vertex
边声明为包含其他Vertex
值的其他列表,这意味着每次将新顶点添加到列表中时,复制构造函数都会立即启动并开始复制该vert
的所有边,这也意味着复制该Vertex
的所有子级,依此类推......
如您所见,这种贪得无厌的复制意味着随着图形变得越来越密集,我们的复制会逐渐花费更多的时间,如果我们决定在图形中引入一个循环,上帝会帮助我们,因为这意味着我们将在该程序完成之前耗尽内存。
将 edges
属性更改为具有此类型:
std::list<std::pair<int, vert&> > edges;
现在这意味着每次我们向图形添加新边时,我们只需维护对其他顶点的引用,而不是复制它们。这也意味着我们实际上能够检测到何时从另一个顶点移除了一条边。
此外,我实际上将此边缘定义为:
std::list<std::pair<int, const vert&> > edges;
这只是一个很好的做法,因为我们希望确保边是不可变的,并且引用另一个顶点并不意味着能够更改其属性。
- CMake-按正确顺序将项目与C运行时对象文件链接
- 通过switch和static_cast访问多态对象的运行时类型
- "main"函数堆栈中的对象在第一个任务运行时被覆盖 (FreeRTOS)
- cout 新创建的对象引发运行时错误
- C++,如何根据运行时条件构造引用不同 istream 对象的对象?
- 创建 QtChart 对象会在运行时创建EXC_BAD_ACCESS错误
- 如何在运行时在对象数组中动态追加新对象C++并打印它们
- 如何在运行时创建和命名对象?
- C++ 在运行时选择一个随机对象
- 在运行时 c++ 更改用类对象填充的数组的大小
- C++ 在运行时获取具有 ID 的对象
- 如何在运行时从 c++ 中的 DLL 文件创建对象?
- 为什么 Z3 BitVec 对象没有运行时大小信息?
- 未知大小的数组作为类成员,用于在运行时(对象创建时间)创建数组的对象
- 是否有任何可能的方法将事件(自定义)附加到在运行时创建的对象?[C 构建器]
- 在C 中运行时从指针访问对象实例
- 如何在运行时指定对象类型
- 根据用户的输入在运行时创建对象
- 为什么C++不按运行时类型销毁对象?
- 根据用户配置在运行时链接共享对象