class 内部与外部定义函数的性能
Performance of defining functions inside a class vs. outside
我正在编写一个程序来玩棋盘游戏 Quoridor。我正在使用 minimax 搜索和自定义 class 来存储状态。我的问题是:定义函数 A. 在 class 内或 B. 在 class 外并传递指针是否有任何性能差异。
我的直觉(这很可能是错误的)是在情况 A 中,它会导致一些内存开销,因为 class 的每个对象都有其自己的所有函数副本。这也会减慢创建对象的速度,因为除了数据成员之外,还必须创建函数。
我的另一个问题是,对于构造函数返回的默认对象 - 使用构造函数或复制预先存在的对象更快吗?
这是 class 的代码:
class BGraph {
public:
int rows;
int columns;
int walls;
int row1;
int column1;
int walls1;
int row2;
int column2;
int walls2;
float time;
// Stores whether a wall has been placed at a vertex.
vector<int> wallsList;
vector<set<int> > edges;
void addEdge(int v1, int v2) {
if (v1 > v2) {
int t2 = v2;
v2 = v1;
v1 = t2;
}
edges[v1].insert(v2);
}
void removeEdge(int v1, int v2) {
if (v1 > v2) {
int t2 = v2;
v2 = v1;
v1 = t2;
}
edges[v1].erase(v2);
}
bool checkEdge(int v1, int v2) {
if (v1 > v2) {
int t2 = v2;
v2 = v1;
v1 = t2;
}
return edges[v1].find(v2) != edges[v1].end();
}
//Take row and column, return the vertex number.
//For example, 1,1 -> 1, 2,1 -> row_length + 1
int rc2v(int r, int c) {
return columns * (r - 1) + c;
}
//Take vertex number, return (row,column)
pair<int, int> v2rc(int v) {
pair<int, int> ans;
ans.first = v / columns + 1;
ans.second = v % columns;
if (ans.second == 0) {
ans.second = columns;
}
return ans;
}
void addWall(Wall wall) {
if (wall.horizontal) {
removeEdge(rc2v(wall.row, wall.column - 1), rc2v(wall.row, wall.column));
removeEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row - 1, wall.column));
wallsList[rc2v(wall.row, wall.column)] = 1;
} else {
removeEdge(rc2v(wall.row - 1, wall.column), rc2v(wall.row, wall.column));
removeEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row, wall.column - 1));
wallsList[rc2v(wall.row, wall.column)] = 2;
}
}
//There are two conditions - the edges that it is trying to break should EXIST.
bool wallIsLegal(Wall wall) {
if (wall.row <= rows && wall.row > 1 && wall.column <= columns && wall.column > 1) {
if (wall.horizontal) {
return checkEdge(rc2v(wall.row, wall.column - 1), rc2v(wall.row, wall.column))
& checkEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row - 1, wall.column))
& wallsList[rc2v(wall.row, wall.column)];
} else {
return checkEdge(rc2v(wall.row - 1, wall.column), rc2v(wall.row, wall.column))
& checkEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row, wall.column - 1))
& wallsList[rc2v(wall.row, wall.column)];
}
} else {
return false;
}
}
//Removes/adds edges to account for the presence of a player.
void adjustForPlayer(int player){
int i,j;
if (player == 1) {
i = row1;
j = column1;
} else {
i = row2;
j = column2;
}
bool e1 = (checkEdge(rc2v(i, j - 1), rc2v(i, j))); //h-x
bool e2 = (checkEdge(rc2v(i, j), rc2v(i, j + 1))); //x-d
bool e3 = (checkEdge(rc2v(i - 1, j), rc2v(i, j))); //b-x
bool e4 = (checkEdge(rc2v(i, j), rc2v(i + 1, j))); //f-x
if (e1 && e2) {
removeEdge(rc2v(i, j - 1), rc2v(i, j));
removeEdge(rc2v(i, j), rc2v(i, j + 1));
addEdge(rc2v(i, j - 1), rc2v(i, j + 1));
} else if (e1 && !e2) {
removeEdge(rc2v(i, j - 1), rc2v(i, j));
if (e3)
addEdge(rc2v(i, j - 1), rc2v(i - 1, j));
if (e4)
addEdge(rc2v(i, j - 1), rc2v(i + 1, j));
} else if (!e1 && e2) {
removeEdge(rc2v(i, j), rc2v(i, j + 1));
if (e3)
addEdge(rc2v(i - 1, j), rc2v(i, j + 1));
if (e4)
addEdge(rc2v(i + 1, j), rc2v(i, j + 1));
}
if (e3 && e4) {
removeEdge(rc2v(i - 1, j), rc2v(i, j));
removeEdge(rc2v(i, j), rc2v(i + 1, j));
addEdge(rc2v(i - 1, j), rc2v(i + 1, j));
} else if (e3 && !e4) {
removeEdge(rc2v(i - 1, j), rc2v(i, j));
if (e1)
addEdge(rc2v(i - 1, j), rc2v(i, j - 1));
if (e2)
addEdge(rc2v(i - 1, j), rc2v(i, j + 1));
} else if (!e3 && e4) {
removeEdge(rc2v(i, j), rc2v(i + 1, j));
if (e1)
addEdge(rc2v(i + 1, j), rc2v(i, j - 1));
if (e2)
addEdge(rc2v(i + 1, j), rc2v(i, j + 1));
}
}
BGraph(int r, int c, int k, int t) {
rows = r;
columns = c;
walls = k;
time = t;
row1 = r;
column1 = c / 2 + 1;
walls1 = k;
row2 = 1;
column2 = c / 2 + 1;
walls2 = k;
//r*c+1 so that we don't have to worry about the fact that these things are actually indexed from zero.
wallsList = vector<int>(r*c+1, 0);
for (int i = 1; i <= r * c + 1; i++) {
set<int> s;
}
//Initialise horizontal edges.
for (int i = 1; i <= r; i++) {
for (int j = 1; j < c; j++) {
addEdge(rc2v(i, j), rc2v(i, j + 1));
}
}
//Initialise vertical edges.
for (int j = 1; j <= c; j++) {
for (int i = 1; i < r; i++) {
addEdge(rc2v(i, j), rc2v(i + 1, j));
}
}
}
//Copy Constructor
BGraph(const BGraph & obj){
rows = obj.rows;
columns = obj.columns;
walls = obj.walls;
row1 = obj.row1;
column1 = obj.column1;
walls1 = obj.walls1;
row2 = obj.row2;
column2 = obj.column2;
walls2 = obj.walls2;
time = obj.time;
wallsList = obj.wallsList;
edges = obj.edges;
}
};
关于你的两个问题:
1) 在 class 内部/外部定义函数是否有任何性能差异?
如果将函数定义为非虚拟函数,那么在性能方面与在 class 之外定义函数没有任何区别。
每个成员非静态函数都有一个隐式参数 this ,这很容易导致一个函数的原型,该函数的指针作为在 class.
之外定义的参数
示例代码:
class A {
void foo(){};
}
void outsideFoo(A* ptr);
// A::foo() true signature is A::foo(A* this)
// which is identical to outsideFoo(A* ptr)
非虚函数在编译时解析,因此您将拥有相同数量的间接寻址。
但是,如果要将函数设为虚拟,则它们将比在 class 之外定义函数慢,因为在运行时将在虚拟函数 table 内部进行搜索,然后跳转到函数 .
对于你的情况,我建议你在 class 中使用非虚拟函数,因为它会使代码清晰。
2) 构造函数与复制构造函数
在你的情况下,它们都一样快,因为你正在制作深拷贝。
调用构造函数与复制构造函数的性能差异由实现定义。例如,如果您要对数据进行一些改进,例如生成数据的空间划分或数据排序,那么复制构造函数会更快,因为它只会复制已经生成的空间划分/排序数据。
但是,如果您直接在两者中分配/赋值,性能将是相同的。
我正在编写一个程序来玩棋盘游戏 Quoridor。我正在使用 minimax 搜索和自定义 class 来存储状态。我的问题是:定义函数 A. 在 class 内或 B. 在 class 外并传递指针是否有任何性能差异。
我的直觉(这很可能是错误的)是在情况 A 中,它会导致一些内存开销,因为 class 的每个对象都有其自己的所有函数副本。这也会减慢创建对象的速度,因为除了数据成员之外,还必须创建函数。
我的另一个问题是,对于构造函数返回的默认对象 - 使用构造函数或复制预先存在的对象更快吗?
这是 class 的代码:
class BGraph {
public:
int rows;
int columns;
int walls;
int row1;
int column1;
int walls1;
int row2;
int column2;
int walls2;
float time;
// Stores whether a wall has been placed at a vertex.
vector<int> wallsList;
vector<set<int> > edges;
void addEdge(int v1, int v2) {
if (v1 > v2) {
int t2 = v2;
v2 = v1;
v1 = t2;
}
edges[v1].insert(v2);
}
void removeEdge(int v1, int v2) {
if (v1 > v2) {
int t2 = v2;
v2 = v1;
v1 = t2;
}
edges[v1].erase(v2);
}
bool checkEdge(int v1, int v2) {
if (v1 > v2) {
int t2 = v2;
v2 = v1;
v1 = t2;
}
return edges[v1].find(v2) != edges[v1].end();
}
//Take row and column, return the vertex number.
//For example, 1,1 -> 1, 2,1 -> row_length + 1
int rc2v(int r, int c) {
return columns * (r - 1) + c;
}
//Take vertex number, return (row,column)
pair<int, int> v2rc(int v) {
pair<int, int> ans;
ans.first = v / columns + 1;
ans.second = v % columns;
if (ans.second == 0) {
ans.second = columns;
}
return ans;
}
void addWall(Wall wall) {
if (wall.horizontal) {
removeEdge(rc2v(wall.row, wall.column - 1), rc2v(wall.row, wall.column));
removeEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row - 1, wall.column));
wallsList[rc2v(wall.row, wall.column)] = 1;
} else {
removeEdge(rc2v(wall.row - 1, wall.column), rc2v(wall.row, wall.column));
removeEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row, wall.column - 1));
wallsList[rc2v(wall.row, wall.column)] = 2;
}
}
//There are two conditions - the edges that it is trying to break should EXIST.
bool wallIsLegal(Wall wall) {
if (wall.row <= rows && wall.row > 1 && wall.column <= columns && wall.column > 1) {
if (wall.horizontal) {
return checkEdge(rc2v(wall.row, wall.column - 1), rc2v(wall.row, wall.column))
& checkEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row - 1, wall.column))
& wallsList[rc2v(wall.row, wall.column)];
} else {
return checkEdge(rc2v(wall.row - 1, wall.column), rc2v(wall.row, wall.column))
& checkEdge(rc2v(wall.row - 1, wall.column - 1), rc2v(wall.row, wall.column - 1))
& wallsList[rc2v(wall.row, wall.column)];
}
} else {
return false;
}
}
//Removes/adds edges to account for the presence of a player.
void adjustForPlayer(int player){
int i,j;
if (player == 1) {
i = row1;
j = column1;
} else {
i = row2;
j = column2;
}
bool e1 = (checkEdge(rc2v(i, j - 1), rc2v(i, j))); //h-x
bool e2 = (checkEdge(rc2v(i, j), rc2v(i, j + 1))); //x-d
bool e3 = (checkEdge(rc2v(i - 1, j), rc2v(i, j))); //b-x
bool e4 = (checkEdge(rc2v(i, j), rc2v(i + 1, j))); //f-x
if (e1 && e2) {
removeEdge(rc2v(i, j - 1), rc2v(i, j));
removeEdge(rc2v(i, j), rc2v(i, j + 1));
addEdge(rc2v(i, j - 1), rc2v(i, j + 1));
} else if (e1 && !e2) {
removeEdge(rc2v(i, j - 1), rc2v(i, j));
if (e3)
addEdge(rc2v(i, j - 1), rc2v(i - 1, j));
if (e4)
addEdge(rc2v(i, j - 1), rc2v(i + 1, j));
} else if (!e1 && e2) {
removeEdge(rc2v(i, j), rc2v(i, j + 1));
if (e3)
addEdge(rc2v(i - 1, j), rc2v(i, j + 1));
if (e4)
addEdge(rc2v(i + 1, j), rc2v(i, j + 1));
}
if (e3 && e4) {
removeEdge(rc2v(i - 1, j), rc2v(i, j));
removeEdge(rc2v(i, j), rc2v(i + 1, j));
addEdge(rc2v(i - 1, j), rc2v(i + 1, j));
} else if (e3 && !e4) {
removeEdge(rc2v(i - 1, j), rc2v(i, j));
if (e1)
addEdge(rc2v(i - 1, j), rc2v(i, j - 1));
if (e2)
addEdge(rc2v(i - 1, j), rc2v(i, j + 1));
} else if (!e3 && e4) {
removeEdge(rc2v(i, j), rc2v(i + 1, j));
if (e1)
addEdge(rc2v(i + 1, j), rc2v(i, j - 1));
if (e2)
addEdge(rc2v(i + 1, j), rc2v(i, j + 1));
}
}
BGraph(int r, int c, int k, int t) {
rows = r;
columns = c;
walls = k;
time = t;
row1 = r;
column1 = c / 2 + 1;
walls1 = k;
row2 = 1;
column2 = c / 2 + 1;
walls2 = k;
//r*c+1 so that we don't have to worry about the fact that these things are actually indexed from zero.
wallsList = vector<int>(r*c+1, 0);
for (int i = 1; i <= r * c + 1; i++) {
set<int> s;
}
//Initialise horizontal edges.
for (int i = 1; i <= r; i++) {
for (int j = 1; j < c; j++) {
addEdge(rc2v(i, j), rc2v(i, j + 1));
}
}
//Initialise vertical edges.
for (int j = 1; j <= c; j++) {
for (int i = 1; i < r; i++) {
addEdge(rc2v(i, j), rc2v(i + 1, j));
}
}
}
//Copy Constructor
BGraph(const BGraph & obj){
rows = obj.rows;
columns = obj.columns;
walls = obj.walls;
row1 = obj.row1;
column1 = obj.column1;
walls1 = obj.walls1;
row2 = obj.row2;
column2 = obj.column2;
walls2 = obj.walls2;
time = obj.time;
wallsList = obj.wallsList;
edges = obj.edges;
}
};
关于你的两个问题:
1) 在 class 内部/外部定义函数是否有任何性能差异?
如果将函数定义为非虚拟函数,那么在性能方面与在 class 之外定义函数没有任何区别。
每个成员非静态函数都有一个隐式参数 this ,这很容易导致一个函数的原型,该函数的指针作为在 class.
之外定义的参数示例代码:
class A {
void foo(){};
}
void outsideFoo(A* ptr);
// A::foo() true signature is A::foo(A* this)
// which is identical to outsideFoo(A* ptr)
非虚函数在编译时解析,因此您将拥有相同数量的间接寻址。
但是,如果要将函数设为虚拟,则它们将比在 class 之外定义函数慢,因为在运行时将在虚拟函数 table 内部进行搜索,然后跳转到函数 .
对于你的情况,我建议你在 class 中使用非虚拟函数,因为它会使代码清晰。
2) 构造函数与复制构造函数
在你的情况下,它们都一样快,因为你正在制作深拷贝。
调用构造函数与复制构造函数的性能差异由实现定义。例如,如果您要对数据进行一些改进,例如生成数据的空间划分或数据排序,那么复制构造函数会更快,因为它只会复制已经生成的空间划分/排序数据。
但是,如果您直接在两者中分配/赋值,性能将是相同的。