我很好奇在访问矢量时哪种方法更快。
为简单起见,我假设我有两个对象:Player
和Ship
。
有一个玩家指针vector<Player*> players
的向量,每个玩家对象都包含一个船舶指针vector<Ship*> ships
的向量,然后每艘船都有几个可以调用的函数,依此类推。
在这些情况下,直接访问这些功能会更快吗?或者创建一个临时对象指针来访问所有内容?
这样做会更快:
for (int i = 0; i < players.size(); i++)
{
for (int j = 0; j < players.at(i)->ships.size(); j++)
{
players.at(i)->ships.at(j)->update();
if (
(players.at(i)->ships.at(j)->get_x() > 0) &&
(players.at(i)->ships.at(j)->get_x() < screen_x) &&
(players.at(i)->ships.at(j)->get_y() > 0) &&
(players.at(i)->ships.at(j)->get_y() < screen_y)
)
{
players.at(i)->visible.push_back(j);
}
}
}
或者创建临时指针是否更快,以便不需要持续访问向量:
for (int i = 0; i < players.size(); i++)
{
Player* play = players.at(i);
for (int j = 0; j < play->ships.size(); j++)
{
Ship* ship = play->ships.at(j);
ship->update();
int ship_x = ship->get_x();
int ship_y = ship->get_y();
if (
(ship_x > 0) &&
(ship_x < screen_x) &&
(ship_y > 0) &&
(ship_y < screen_y)
)
{
play->visible.push_back(j);
}
}
}
我知道第二个是视觉上更整洁,但不知道它是否必然更快。
思想?
答案 0 :(得分:2)
在我看来,对速度的强调是错误的。我认为你应该首先编写代码以使其更具可读性:
auto is_visible = [=](Ship const &s) { return s.get_x() > 0 && s.get_x() < screen_x
&& s.get_y() > 0 && s.get_y() < screen_y;
};
for (auto & player : players)
std::copy_if(ships.begin(), ships.end(),
std::back_inserter(player.visible),
is_visible);
至少IMO,这至少与使用at
进行索引一样安全,但可能至少与使用[]
一样快,并且比任何一个都更具可读性。
我应该补充一点:可见性似乎并不取决于玩家。至少从编写代码的方式来看,所有玩家都拥有相同的可见船只。如果这是正确的,你可能想要做更多的事情:
std::vector<Ship> visible;
std::copy_if(ships.begin(), ships.end(),
std::back_inserter(visible),
[=](Ship const &s) { return s.get_x() > 0 && s.get_x() < screen_x
&& s.get_y() > 0 && s.get_y() < screen_y; });
for (auto &player : players)
player.visible = visible;
答案 1 :(得分:1)
你应该检查哪一个更快。
可能是第一个,也可能是第二个。如果大多数船只的X坐标为负,那肯定是第一次。
然而,如果第二个对你来说看起来更好(对我来说也是如此),坚持下去。在出现实际性能问题时担心性能。
答案 2 :(得分:1)
我认为你在这里优化编译器的优势。任何一个都可能更快,取决于它如何优化。
在第一个版本中,编译器可能决定使用它
拉出players.at(i)->ships.at(j)
公共子表达式,
可能会将get_x()
或get_y()
变成某种东西
看起来很像你的第二个版本。
在第二个版本中,重新排序可能会移动
int ship_y = ship->get_y()
进入循环条件,以便它可以
与ship_y > 0
短路。
在两者中,它可能决定将整个短路变为有条件的 进入一系列快速按位和指令,消除 分支
但我的猜测是,你不会在任何方面看到太多差异。尝试转储汇编代码进行比较,当然还要对其进行分析。
答案 3 :(得分:0)
感谢所有人的信息。由于没有明确的选项“选项A 比选项B更快”,我接受了你的建议并进行了替补测试。
这是我放在一起的一些代码。
基本上,它创造了100名球员。每个玩家都有100艘船的向量。每艘船都有100名船员。 (一旦运行,它消耗了大约500MB的RAM)。
我运行了未经优化和优化的测试(-O3标志)
测试1是链指针(即播放器 - &gt; ship-&gt; crew-&gt;数字等) 测试2与测试1相同,但我用operator []替换了所有.at()。 测试3使用临时指针访问所有内容。
我多次运行每个测试并对结果取平均值。 这是我的结果:
未优化:
Test 1: 13000
Test 2: 5500
Test 3: 2800
优化
Test 1: 1050
Test 2: 650
Test 3: 450
它表明在所有情况下优化都会大大提高速度。 无论哪种方式,优化或未经优化,.at()肯定会减慢速度。使用operator []明显更快。 但最后,在所有情况下使用临时指针是最快的。
#include <vector>
#include <ctime>
#include <iostream>
using namespace std;
class People
{
public:
vector<int> number;
};
class Ship
{
public:
Ship(int f);
vector<People*> crew;
int get_x();
int get_y();
private:
int x;
int y;
};
Ship::Ship(int f)
{
//Assign some nonsense for testing purposes
x = f * 50;
y = f * 75;
}
int Ship::get_x()
{
return x;
}
int Ship::get_y()
{
return y;
}
class Player
{
public:
vector<Ship*> ships;
};
int main(int argc, char *argv[])
{
vector<Player*> players;
int start, end;
unsigned int i, j, k, l;
//Create 100 players, each with 100 ships, and each ship with 100 crew.
for (i = 0; i < 100; i++)
{
Player* play = new Player;
players.push_back(play);
for (j = 0; j < 100; j++)
{
Ship* new_ship = new Ship(j);
play->ships.push_back(new_ship);
for (k = 0; k < 100; k++)
{
People* newbie = new People;
new_ship->crew.push_back(newbie);
for (l = 0; l < 100; l++)
{
newbie->number.push_back(0);
}
newbie->number.clear();
}
}
}
//Test 1
start = clock();
for (i = 0; i < players.size(); i++)
{
for (j = 0; j < players.at(i)->ships.size(); j++)
{
for (k = 0; k < players.at(i)->ships.at(j)->crew.size(); k++)
{
for (l = 0; l < 100; l++)
{
//Give each crew some number to hold on to.
players.at(i)->ships.at(j)->crew.at(k)->number.push_back(players.at(i)->ships.at(j)->get_x() * players.at(i)->ships.at(j)->get_y() + l);
}
//Clear the number list for the next test.
players.at(i)->ships.at(j)->crew.at(k)->number.clear();
}
}
}
end = clock();
cout << "Test 1: " << (end - start) << endl;
//Test 2
start = clock();
for (i = 0; i < players.size(); i++)
{
for (j = 0; j < players[i]->ships.size(); j++)
{
for (k = 0; k < players[i]->ships[j]->crew.size(); k++)
{
for (l = 0; l < 100; l++)
{
players[i]->ships[j]->crew[k]->number.push_back(players[i]->ships[j]->get_x() * players[i]->ships[j]->get_y() + l);
}
players[i]->ships[j]->crew[k]->number.clear();
}
}
}
end = clock();
cout << "Test 2: " << (end - start) << endl;
//Test 3
start = clock();
for (i = 0; i < players.size(); i++)
{
Player* temp_play = players.at(i);
for (j = 0; j < temp_play->ships.size(); j++)
{
Ship* temp_ship = temp_play->ships.at(j);
for (k = 0; k < temp_ship->crew.size(); k++)
{
People* temp_crew = temp_ship->crew.at(k);
for (l = 0; l < 100; l++)
{
temp_crew->number.push_back(temp_ship->get_x() * temp_ship->get_y() + l);
}
temp_crew->number.clear();
}
}
}
end = clock();
cout << "Test 3: " << (end - start) << endl;
return 0;
}