Recycling variables and performance

Started by
31 comments, last by mrbastard 12 years, 10 months ago

How can you say this with a straight face? A proper foreach? You call std::for_each a proper foreach?

C# has a proper foreach. Requiring one to provide a function object is not a proper foreach.


Yes, C# has better primitive support for the foreach construct than C++. However, the fact that the C++ foreach must be implemented using a function object does not make it "not a proper" foreach, as this is an implementation issue stemming from the lack of an existing language primitive.


The closest you can get to a proper foreach is using C++0x's lamba syntax:

Leaves a lot to be desired.

I'd like a real foreach in C++, but std::for_each certainly isn't one.
[/quote]

It is a foreach implemented using the language primitives of C++. It is "proper." There are language deficiencies that prevent the syntax from being ideal, but it still achieves the goals of what the foreach construct semantically means.


Using a built-in control structure to iterate over a container and using a higher-order construct that calls a function object on each member of a container are not the same thing.
[/quote]

If by "not the same thing" you mean the code looks different, then I agree. However, one can achieve the exact same effect as the other, and there is a potential semantic difference.


It's absolutely disingenuous to claim that std::for_each is a replacement for the temporary solution of using a for loop. The syntax was not "retrofitted to emulate the foreach construct." Not only does that not make any sense, you can't provide any evidence for it. The iterator syntax is the way it is in order to emulate pointers from C, which already had iterator semantics.
[/quote]

Using a for loop to iterate over the contents of a C++ standard library collection does not imply the same semantics as a foreach construct in other languages. Easily the first difference is that C++ will happily allow you to modify the collection in the body of the loop without batting an eye, leading to a lot of threads posted on GameDev.net. The for loop was not envisioned as an analog to the foreach concept, and thus using a for loop in place of a foreach was simply out of necessity.


Stroustrup himself prescribes a for loop as a "way of traversing a vector." See this powerpoint presentation, page 37.
http://www.stroustru..._containers.ppt
[/quote]

Of course you can, and for most basic case, I would as well. I am not saying it is incorrect, I am saying that it is semantically different than the foreach concept. The whole point that I am trying to make is that there is a distinction between the use of the for loop as a foreach replacement using iterators, and the use of the for loop as a counted loop. The former is simply a language difficiency, and its syntax usage is not necessarily indicative of idiomatic syntax for a numeric based for loop.
Advertisement

Are you being serious here? You are implying that one should write a function object to represent the internals of what in the past would have been a for loop? We should just have hundreds of function objects all over our code? If you're seriously implying this then I'd hate to see your code.


In most cases, yes, I prefer for_each usage to for usage when traversing a collection, however, I don't appreciate C++'s lack of primitive support for the foreach construct. This is getting away from the actual meat of my argument, which is that the syntax of a C++ for loop when emulating a foreach is not necessarily an argument in support of the same syntax when using a for loop as a numeric counted loop.

The argument about stopping infinite loops in the case of a bad terminal value doesn't hold much water --- if you've got a bad terminal value, then your code is wrong. Making wrong code look like it's working is a very bad thing.

Introducing a potential buffer overflow is likely an even worse thing, from a security standpoint. Loop indices are not C++ iterators (they can't be independently dereferenced), where the i != end idiom is necessary (input iterators don't even necessairly have an operator<). While I use != with pointer ranges myself, the argument holds plenty of water even there.


If I break something, I will instantly know because i write so that problems will show themselves at compile time.

I am unsure as to how (10 != i) will produce a compile time error as opposed to (i < 10).
[/quote]

That wasn't very specific of me either, I meant in general. Obviously both examples would pass the compiler without warnings or errors.


The mere termination of a loop should never be an indication of working code, as the loop should perform some function, and the results of that computation should be the metric by which the correctness of the loop is guaged. However, one syntax allows you to terminate when you cannot accurately predict the final absolute value by which the loop must terminate. For instance:

for (float i = 0.0f; 2 * PI != i; i += PI / 12.0)

Because of accumulating floating point inaccuracy, this could potentially fail. Also:

n = *some number based on user input that can be any positive whole number in the range 10 to 15 *
for (int i = 0; n != i; i += 3) // we want this to loop until i exceeds the user input value

I completely agree, -i also stated that I would use < and > for floats in my second post. I always do that, I can't even be sure that 0.9f + 0.1f == 1.0f, so it would be silly to check for that specific value.




Happy for your input although this thread has somewhat strayed from the original purpose. ;-)

I certainly hope that you do not take this the wrong way, as I am a language nerd and will politely discuss any range of minutiae ad nauseum. No one is incorrect here.
[/quote]
Not at all. It's nice to discuss language when software design discussions gets a little tiresome, -and vice-versa. :)
As stated earlier, I've also taken the point with regards to that, "if you can remember to put the const value first, you can also remember to put == rather than =".

Are you being serious here? You are implying that one should write a function object to represent the internals of what in the past would have been a for loop? We should just have hundreds of function objects all over our code? If you're seriously implying this then I'd hate to see your code.


Yes, we absolutely should. It may be a lambda, but it should certainly be a function object.

Why? Because this allows you to use std algorithms (as well as algorithms from boost, TBB, PPL, and your own template algorithms). This is worthwhile purely for clarity - the function name tells you the number of iterations on the first line of the loop. For/while etc do not do this - you have to examine the body of the loop.

E.G.


for(size_t i=0; i<10; ++i)
{
//can break
//can increment or decrement i
//can continue
//can return
}

//so the above may do 10 iterations, or it may do more, or less, and may not execute the entire body every time



std::for_each(begin, end, [&]( const valT& val)
{
//do stuff
});

//whereas here we know for a fact we are doing distance( begin, end ) iterations.
//And we know this from reading only the first line.
[size="1"]

I completely agree, -i also stated that I would use < and > for floats in my second post. I always do that, I can't even be sure that 0.9f + 0.1f == 1.0f, so it would be silly to check for that specific value.


Also, to clarify what I probably have said poorly in my other posts (this is what happens when I post past my bedtime :( ) think about when you are trying to check whether a a number is within a specified range, you would use something like:


if ((i > 0) && (i < 100))


The < and > specifiy boundary conditions, and when I read a for loop, it seems natural to me to read it as for (start value; boundary; sequence function).


Not at all. It's nice to discuss language when software design discussions gets a little tiresome, -and vice-versa. :)
As stated earlier, I've also taken the point with regards to that, "if you can remember to put the const value first, you can also remember to put == rather than =".
[/quote]

I don't mind consts placed first when dealing with equality (except for some reason with null, I just feel that it looks and reads wrong :angry: ). And I am perfectly capable of reading your code, so I think you are doing just fine. I don't mind strange conventions as long as there is consistancy!

Why? Because this allows you to use std algorithms (as well as algorithms from boost, TBB, PPL, and your own template algorithms). This is worthwhile purely for clarity - the function name tells you the number of iterations on the first line of the loop. For/while etc do not do this - you have to examine the body of the loop.


This is a great point and is certainly an advantage of the foreach paradigm. Trust me, I'm a fan of it. I'm a fan of function objects, too (though I prefer them to be first-class objects). That said, I can't agree that it makes the code clearer in the case that the language forces you to use a function object rather than the body of a loop. I can learn to live with the C++0x lambda syntax with std::for_each, but code in which almost every loop is a for_each with a function object is not clear code. It is easier for me to examine the body of a for-loop to see whether it has any break or continue statements than it is to have to leave the code I'm currently looking at to navigate to class SomeRandomFunctorICreatedJustForThisOneLoop.


I guess we'd just have to agree to disagree, but I'd wager you guys don't practice what you preach. I bet your code has for loops rather than for_each with function objects all over the place, because I don't think any logical person would consider that clear style.

class SomeRandomFunctorICreatedJustForThisOneLoop.


I guess we'd just have to agree to disagree, but I'd wager you guys don't practice what you preach. I bet your code has for loops rather than for_each with function objects all over the place, because I don't think any logical person would consider that clear style.


JustForThisOneLoop is the key here. I went through a phase of almost always using functors, even if I only needed the body once. As you imply, that's just not practical. As soon as you use the loop body more than a couple of times it becomes worth having a functor you can reuse - predicates especially tend to get into this category pretty fast. I think we probably still agree so far. I definitely agree that jumping away from the loop to find a random functor class for absolutely every loop would be an annoyance (though you don't have to do it as often as you may think, because of not having to check for the loop condition changing, etc). But it's not a stretch to remember what a small set of functors do, especially if they're simple - think std::max and the like. It's rare for me to have more than two or three functors for a certain cpp file or group of related classes, so there's very little overhead. With a small vocabulary of functors for your problem area, it can actually be clearer style, if anything. I find it easier to read 'max' than 'x>y?x:y;', for example.

That was before. Now with c++0x, I have no reason(*) to write a plain loop - I can use a lambda every time, and then factor that lambda out into a standalone functor if and when it becomes useful to reuse.

(*) - except for performance, of course. I spent some time emulating python's xrange, so I could iterate over numeric ranges using for_each etc. In most places this is fine for me, but in my inner loops it was around 10% faster to use a plain for with a counter. YMMV

Sorry to the OP for derailing the thread btw unsure.gif
[size="1"]

I'd like a real foreach in C++, but std::for_each certainly isn't one.


Me too, but I don't really think it is feasible really. It would be a real mess if the language core itself had to be aware of some kind of enumerable interface that a container would have to fulfill and it would be a horrible special case for native arrays and so on. Just too many corner cases in this (as the oddly vanished Toohrvyk used to say) language of octopus formed by welding legs onto dog.

I've personally found the new auto keyword has taken most of the pain out of just using a for loop. It isn't really that much typing. Not really used lambdas myself yet but for anything except a very trivial operation, I find a normal for loop with a braced loop section far easier to read.

Maybe I'm just not down with the cool kidz though. :)

[EDIT] Although, just spotted mrbastard's comment above, which is certainly very true.
Either way, the original question is about register allocation. I remember when this was taught in a mostly useless course on algebra and was covered in an utterly useless sentence: "Graph coloring has application in compiler implementations". Explaining why or how was apparently left as exercise to the student.

C and C++ are one of few languages, partly due to lack of reflection and introspection that are free to rearrange all auto-allocated objects as they see fit, or even not allocate them. The answer to original question is therefore: It depends solely on degree and type of optimization a compiler performs.

C used to have 'register' keyword which meant precisely that - allocate variable in register. Aside from dealing with hardware it helped optimize the code. But the problem was automated very soon and is present in just about every compiler these days, so OP's question isn't relevant to most compilers these days - they will use minimal number of registers.


When using variable bounds rather than constant, while loop with pointer arithmetic is better since it saves one register:for (int i = 0; i < n; i++) {
a = ...
}
// i, n, a have to be kept in registers

T * curr;
T * end;
while (curr < end) {
*curr = ...
curr++;
}
// only 2 variables

Last I checked under MVS, while loop frees one register, which can result in slightly faster code since there are more available to inner body.

While compiler might be able to perform such optimization, it may choose not to due to potential aliasing or similar issues, I don't know the details.


I believe that kkrunchy (or some similar packer), an exe packer claims to gain a lot of performance and small size due to efficient and manual register usage which compilers aren't capable of. For whichever reason they tend to be conservative with use of certain registers. Generated code today is still mostly in a-dx, with additional ones appearing rarely or only in specific code. It might be related to calling conventions in some way.


As far as theoretical performance, register and resource allocation in general, let alone on real hardware is absurdly hard problem, NP-complete several times over. There was considerable work done (IBM IIRC) that tried all possible sequences of operations up to certain n and try to determine optimal allocation. But then there'sregister renaming, pipeline stalls, various hazards - and that's without leaving registers, when memory comes into play things explode in complexity.

If one is really into computer science and more importantly, hardware, there is a lot to still be explored. I don't think any compiler today even scratches the surface and instead relies on heuristics. Not to mention most of code today runs on software VMs which is many orders of magnitude slower than theoretical throughput, yet applications remain IO bound.

This topic is closed to new replies.

Advertisement