Range based loops and references

hi,
i’m looping through a vector of objects, for that i’m using a for each loop like this:

		for (Semicircle semi : Circles) {
			semi.render();
		}

it works fine and i have no problem with my app.
the thing is i’ve read that it’s recommended when looping to objects like this to use a reference to avoid copies and performance loss, but when looping like this:

		for (Semicircle &semi : Circles) {
			semi.render();
		}

my app goes from 60fps to around 3fps in a few seconds. for this app i’m keeping the loop without the reference because it;s working fine, but did i understand the reference part wrong it wrong?
thanks

It’s difficult to see why this would happen from this code alone. An implicit cast perhaps? You could try type deduction with auto and add const like this:

for (const auto &semi : Circles) {
    semi.render();
}
1 Like

yes i’m sorry, i should’ve given more context to that code.
in .h i declare the vector:

vector<SemiCircle> Circles;

first i’m populating the vector in update():

	if (drawing) {
		for (int j = 0; j < densidadFondo; j++) {
			Circles.push_back(SemiCircle(WIDTH / 2, HEIGHT / 2 * i, sizeFondo - (d * j), color.darColor(), SemiCircle::IZQ));
			Circles.push_back(SemiCircle(WIDTH / 2, HEIGHT / 2 * i, sizeFondo - (d * j), color.darColor(), SemiCircle::DER));
		}

and in draw():

	for (Semicircle semi : Circles) {
			semi.render();
		}

i tried adding const, but then i cannot call the function render(), the errors says “the object has type qualifiers that are not compatible with the member function”.

again, as i said my app works fine like that, my question is about the use of references in range based loops and why i get this performance issue when using references, when it should be even more efficient (if i understood it right)

thank you

I feel like this might be the issue rather than the references problem you mentioned. Did you make sure to allocate your vector with Circles.reserve(N) in setup()?

If you keep adding lots of circle objects on every frame update you eventually reach a point where rendering everything become too costly. It is recommended to fully initialize your vector once in setup() with all (or a maximum) of the circles you expect to use during the lifetime of your program.

Looking at the vector declaration, for (Semicircle &semi : Circles) should work fine.

1 Like

i clear the vector every time before repopulating it with the for loop, but i’m going to try what you suggest. it’s strange that the way i’m doing it it works fine, i don’t experience any frame drop until i use references in the for each loop.
thank you

When you push_back in a vector you are actually inserting a copy of the just-created object (the SemiCircle in parenthesis is temporary). emplace_back() can create the object directly into the vector. However once you exceed whatever you’ve had reserved() it will delete everything and re-allocate the objects in another memory zone. (vectors can grow dynamically, but they need a contiguous piece of RAM so will reallocate their contents as they grow).

If you’re creating and destroying a bunch of SemiCircle every frame it might be better to cache and re-use them. Even if SemiCircle is a “light footprint” class, at 60fps the overhead adds up. While it’s not as critical as, say, in an audio callback, memory allocation in the update/render loop is ill-advised unless artistically necessary.

This is a preamble to your actual question… without analyzing the assembly we can’t say how the compiler optimizes that code, but it “knows” that the SemiCircle objects are created, copied, drawn, and destroyed (i.e. they’re temporary). In theory a reference should be faster because it skips a copy (even better if const), but using the reference probably diminishes the options the compiler has. Maybe working with a temp variable allows it to apply some trick(s) – ultimately the copies can be elided and the render() method of SemiCircle could be unrolled and inlined and make use of registers and whatnot (an executable’s apparent logic can be very different than the expressed high-level code).

My take is that with the copy, the compiler grins and optimizes most of the logic away; with the constraint of the reference it throws the towel and passive-aggressively goes through the loops step-by-step saying “if that’s what you want”.

Beyond that it’s difficult to identify potential side-effect issues with SemiCircle because we did not get to see the source. Please post a complete compilable example, and provide your OS/IDE/compiler version.