std::vector.size()调用导致SFML模拟程序启动缓慢延迟的问题
问题解析:std::vector.size()导致程序启动缓慢的原因
问题背景
我正在学习C++,使用SFML编写圆形碰撞模拟程序。原本圆与墙壁的碰撞功能正常,但添加圆与圆碰撞的双层循环后,程序编译链接完成后启动延迟极高,不过启动后运行流畅。将circles.size()提前存入变量再在循环中使用,程序启动速度恢复正常。
代码对比
原循环代码(启动缓慢)
for (int i = 0; i < circles.size() - 1; i++) { MovingCircle& circle1 = circles[i]; for (int j = i + 1; j < circles.size(); j++) { MovingCircle& circle2 = circles[j]; // 碰撞检测与速度计算逻辑 } }
修改后的代码(启动正常)
int circlesSize = circles.size(); for (int i = 0; i < circlesSize - 1; i++) { MovingCircle& circle1 = circles[i]; for (int j = i + 1; j < circlesSize; j++) { MovingCircle& circle2 = circles[j]; // 碰撞检测与速度计算逻辑 } }
核心原因解析
std::vector.size()的本质
std::vector.size()是O(1)操作,仅返回内部维护的成员变量,单次调用开销极小。但问题出在冷启动阶段的缓存特性与编译器优化的缺失。冷启动阶段的缓存未命中
程序刚启动时,CPU的指令缓存和数据缓存都是空的,所有代码和数据需要从内存加载。此时双层循环中每次调用size(),都会触发一次内存读取操作。虽然单次读取很快,但O(N²)次循环的累积效应,会导致大量缓存未命中,直接拖慢启动速度。
而提前将size()存入变量后,该值会被加载到CPU寄存器中,循环内直接读取寄存器,完全避免了重复的内存访问,冷启动时的缓存压力大幅降低,启动速度恢复正常。
启动后流畅的原因
程序运行一段时间后,常用的代码和数据(包括vector的size成员)已被加载到CPU缓存中。此时即使每次循环调用size(),也会命中缓存,速度与读取寄存器几乎无差别,因此运行流畅。编译器优化的影响
如果使用Release模式编译(开启-O2/-O3优化),编译器会自动将循环内的size()调用提升到循环外,效果和手动存变量一致。但Debug模式下默认无优化,编译器不会做这个优化,导致重复的内存读取。
完整代码
#include <SFML/Graphics.hpp> #include <random> #include <vector> #include <iostream> #include <cmath> struct MovingCircle { sf::CircleShape shape; sf::Vector2f direction; float speed; }; MovingCircle createCircle(sf::Vector2i mousePos, sf::Color color) { MovingCircle newCircle; newCircle.shape = sf::CircleShape(25.0); newCircle.shape.setOrigin( newCircle.shape.getRadius(), newCircle.shape.getRadius()); newCircle.shape.setPosition( static_cast<float>(mousePos.x), static_cast<float>(mousePos.y)); newCircle.shape.setFillColor(color); return newCircle; } sf::Vector2f normalizeVector(sf::Vector2f vector) { float length = std::sqrt(std::pow(vector.x, 2) + std::pow(vector.y, 2)); return vector / length; } float calculateScalarProduct(sf::Vector2f vector1, sf::Vector2f vector2) { return vector1.x * vector2.x + vector1.y * vector2.y; } float calculateNormOfVector(sf::Vector2f vector) { return std::sqrt(std::pow(vector.x, 2) + std::pow(vector.y, 2)); } float calculateLength(sf::Vector2f vector) { return std::sqrt(std::pow(vector.x, 2) + std::pow(vector.y, 2)); } sf::Vector2f calculateProjection(sf::Vector2f a, sf::Vector2f b) { // Projektion von a auf b float scalarProduct_ab = calculateScalarProduct(a, b); float scalarProduct_bb = calculateScalarProduct(b, b); float scale = scalarProduct_ab / scalarProduct_bb; return scale * b; } int main() { std::random_device rd; // für den Seed std::mt19937 gen(rd()); // mit Seed initialisieren std::uniform_int_distribution<> dist(0, 255); std::vector<MovingCircle> circles; sf::RenderWindow window(sf::VideoMode(1000, 1000), "Ballspiel v2"); window.setFramerateLimit(60); sf::View view = window.getDefaultView(); // Um später zu prüfen, ob bereits eine circlePos erstellt wurde, // wird ein Standardwert (-1, -1) festgelegt: // Solange der Standardwert zugewiesen ist, gibt es noch keine // festgelegte circlePos sf::Vector2i circlePos = sf::Vector2i(-1, -1); sf::VertexArray line(sf::Lines, 2); MovingCircle newCircle; float speedFactor = 0.05; while (window.isOpen()) { // EVENTS sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); if (event.type == sf::Event::Resized) { sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height); window.setView(sf::View(visibleArea)); } if (event.type == sf::Event::MouseButtonPressed) { if (event.mouseButton.button == sf::Mouse::Left) { // Es gibt schon eine circlePos if (circlePos != sf::Vector2i(-1, -1)) { sf::Vector2f direction = line[0].position - line[1].position; circles.back().direction = normalizeVector(direction); circles.back().speed = calculateLength(direction); circlePos = sf::Vector2i(-1, -1); } else { // Es gibt noch keine circlePos // circlePos = (-1, -1) circlePos = sf::Mouse::getPosition(window); sf::Color color(dist(gen), dist(gen), dist(gen)); newCircle = createCircle(circlePos, color); circles.push_back(newCircle); } } } } // UPDATE if (circlePos != sf::Vector2i(-1, -1)) { sf::Vector2i mousePos = sf::Mouse::getPosition(window); line[0].position = sf::Vector2f( static_cast<float>(circlePos.x), static_cast<float>(circlePos.y)); line[1].position = sf::Vector2f( static_cast<float>(mousePos.x), static_cast<float>(mousePos.y)); } for (auto& circle : circles) { // Prüfung, ob Kreis in x-Richtung mit Rand kollidiert if (circle.shape.getPosition().x > 975.0) { circle.direction = circle.direction - 2 * calculateScalarProduct(circle.direction, sf::Vector2f(-1.0, 0.0)) * sf::Vector2f(-1.0, 0.0); } else if (circle.shape.getPosition().x < 25.0) { circle.direction = circle.direction - 2 * calculateScalarProduct(circle.direction, sf::Vector2f(1.0, 0.0)) * sf::Vector2f(1.0, 0.0); } // Prüfung, ob Kreis in y-Richtung mit Rand kollidiert if (circle.shape.getPosition().y > 975.0) { circle.direction = circle.direction - 2 * calculateScalarProduct(circle.direction, sf::Vector2f(0.0, -1.0)) * sf::Vector2f(0.0, -1.0); } else if (circle.shape.getPosition().y < 25.0) { circle.direction = circle.direction - 2 * calculateScalarProduct(circle.direction, sf::Vector2f(0.0, 1.0)) * sf::Vector2f(0.0, 1.0); } } int circlesSize = circles.size(); for (int i = 0; i < circlesSize - 1; i++) { MovingCircle& circle1 = circles[i]; for (int j = i + 1; j < circlesSize; j++) { MovingCircle& circle2 = circles[j]; // Collision detection and calculating the velocities after collision } } for (auto& circle : circles) { circle.shape.move(circle.direction * circle.speed * speedFactor); } // DRAW window.clear(); if (circlePos != sf::Vector2i(-1, -1)) { window.draw(line); } for (auto& circle : circles) { window.draw(circle.shape); } window.display(); } return 0; }
内容的提问来源于stack exchange,提问作者Tim
相关产品推荐
相关产品推荐

