Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11: Why does this range loop decrease FPS by 35?

I'm writing a game using SFML and C++11 features, such as the range loop. When working on tile maps, I basically made a class for each map tile, a light-weight class that simply contains its sprite, position, and such, and then built some nested vectors to represent the game map layers.

In order to optimize the process of drawing thousands of objects on the screen at a time, I was simply drawing what the player sees. This went well.

I have the following method that renders the game map, the condition basically returns true if the tile position is within the camera boundaries

void gameMap::render(sf::RenderWindow &winMain, sf::Vector2f offset) {
      for(vector<int> vec1 : backgroundData)
           for(int i : vec1)
              if(collides(i.pos, offset)
                 myWindow.draw(i.sprite);

     }

it works fine, however in-game I am getting 30 FPS roughly, and a lot of rough movement. But what surprises me is that the code bellow does the same thing, renders the same amount of tile sprites, but runs at 65 fps and the movement is perfectly smooth

void gameMap::render(sf::RenderWindow &winMain, sf::Vector2f offset) {
          for(int i = 0; i < backgroundTiles.size(); i++)
               for(int j = 0; j < backgroundTiles[i].size(); j++)
                  if(collides(backgroundTiles[i][j].pos, offset)
                     myWindow.draw(backgroundTiles[i][j].sprite);

         }

Why is this happening? Is the C++11 range-based loop so much slower than the old school for? I really want to hear an answer to this, because my eyes honestly prefer the range based loop, and I'd hate to find out that the range based loop is twice as slow.

like image 206
Zamri Malakun Avatar asked Apr 07 '13 16:04

Zamri Malakun


1 Answers

The outer loop is making a copy of each vector contained in backgroundData:

  for(vector<int> vec1 : backgroundData)

Change that to either of the following:

  for(vector<int>& vec1 : backgroundData)
  for(const vector<int>& vec1 : backgroundData)

This will make vec1 into a reference to the vector as opposed to a copy. Since vectors are expensive to copy, while references are cheap to use, this will significantly improve performance.

As to the choice between non-const and const reference, I'd use the latter whenever I can.

A more general alternative is to write

  for(auto&& vec1 : backgroundData)

This creates an automatically-typed reference vec1 to whatever type backgroundData contains. && in this context ends up making vec1 bind to any of: rvalue reference, reference or const reference, depending on the types that backgroundData returns. [Hat tip to @Yakk for providing this recommendation]

like image 65
NPE Avatar answered Oct 19 '22 02:10

NPE