You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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];

        // 碰撞检测与速度计算逻辑
    }
}

核心原因解析

  1. std::vector.size()的本质
    std::vector.size()是O(1)操作,仅返回内部维护的成员变量,单次调用开销极小。但问题出在冷启动阶段的缓存特性与编译器优化的缺失。

  2. 冷启动阶段的缓存未命中
    程序刚启动时,CPU的指令缓存和数据缓存都是空的,所有代码和数据需要从内存加载。此时双层循环中每次调用size(),都会触发一次内存读取操作。虽然单次读取很快,但O(N²)次循环的累积效应,会导致大量缓存未命中,直接拖慢启动速度。

而提前将size()存入变量后,该值会被加载到CPU寄存器中,循环内直接读取寄存器,完全避免了重复的内存访问,冷启动时的缓存压力大幅降低,启动速度恢复正常。

  1. 启动后流畅的原因
    程序运行一段时间后,常用的代码和数据(包括vector的size成员)已被加载到CPU缓存中。此时即使每次循环调用size(),也会命中缓存,速度与读取寄存器几乎无差别,因此运行流畅。

  2. 编译器优化的影响
    如果使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 23:12:33