Re: References vs variables - speed impacts?
On 8 Nov, 15:51, Rune Allnor <all...@tele.ntnu.no> wrote:
On 7 Nov, 16:20, Nick Hounsome <nick.houns...@googlemail.com> wrote:
On 5 Nov, 18:47, Rune Allnor <all...@tele.ntnu.no> wrote:
Hi all.
I always thought references were mere synonym names to already
existing variables;
size_t long_and_complicated_expression;
size_t& i = long_and_complicated_expression;
Instead of typing the long and complicated name, one gets away
with the short and sweet 'i'.
In the code I am working with now, references appear to behave
as if they were actual variable assignments. Consider the nested
vector indexes
std::vector<size_t> v1;
std::vector<size_t> v2;
size_t n;
{// Unconditional evaluation
const size_t& i = v1[v2[n]];
const size_t& j = v2[v1[n]];
This effectively does something like:
size_t* const j = &( v1.operator[]( v2.operator[]( n ) ) ); //
calling T& vector<T>::operator[](size_t);
NOT
#define j v2[v1[n]]
The cost of this obviously far from 0 but will depend on what checking
you have enabled and how much the compiler can/will inline the calls.
It seem this is the crux. I reviewed the code, and found two
possible (and very likely) obstacles to removal of temporary
variables, which I depend on the compiler to do for me:
1) Type casts. The vector contains objects of that contain
a number of size_t pointers to other elements:
class element{
const size_t a() const; // Read element a
const size_t b() const;
const size_t c() const;
void a(const size_t&); // Modify element a
void b(const size_t&);
void c(const size_t&);
private:
size_t a_;
size_t b_;
size_t c_;
};
First of all, I use const T& as argument type by default
in function calls, so I use the const size_t& signature in
the modifying functions as a matter of course, not speed
reasons.
This is very odd.
You seem to be obssessed with speed and yet you use references
(disguised pointers) for no good reason.
I suggest that you should avoid the references entirely in this
example.
With the signatures above, the layered element access
through temporary object goes like
size_t k,m,n;
std::vector<element> v;
size_t tmp = v[n].a();
v[k].b(tmp);
As far as I can tell, the typecast of tmp that happens in
the call to element::b(const size_t&), from size_t to size_t&,
might be enough to stop the optimizer from optimizing the
temporary variable tmp away from the executable code.
There is no type cast.
A reference to the temporary is passed.
2) Changing const elements. I declare my temporary variables
like
const size_t& tmp1 = v[n].a();
const size_t& tmp2 = v[m].b();
const size_t& tmp3 = v[k].c();
This is very bad programming style.
Never retain external references to the internals of an object.
Returning a reference to member should be treated ONLY as an efficient
alternative to return by value of non-trivial types.
This is because otherwise you constrain the implementation of the
class - In this example you cannot change the type of a_.
Then I start modifying the elements:
// Use tmp1
// The *value* referred to tmp1 is now obsolete, and
// never used again. The *variable* referred to by
// tmp1 will be modified:
tmp3 = v[tmp2].c();
v[n].a(tmp3); // << === element referred to by tmp1
// changes here
I don't think so. and even if it did it is hopelessly confusing.
I didn't realize it when I coded up these things, but I
assume such constructs put too much strain on the optimizer,
so it might be more likely to back down, leaving the code as
written, instead of attempting to do the optimizations I
need it to do.
If any of these hypotheses are close to what actually happens,
it would explain why explicit calls without temporary variables,
like
v[n].a(v[m].b()); // In the actual code there can be 3-5 nested
// layers of dereferences, both in the index
// indicated by 'n' here, and in the argument
// to the modifyer function. Which makes the
// code unreadable. Which is why I wanted to
// use the temporaries, but with somewhat more
// descriptive names than indicated above.
I could well be wrong because this isn't your real code but if it is
similar then I suspect that your design is not good.
The depth of nesting may well also be significant.
execute orders of magnitudes faster: This call is explicit,
unequivocal, and can easily be optimized whithout the
optimizer having to impose unwarranted assumptions on the
use of the code.
I find it hard to imagine a real world situation where the sort of
optimization you are talking about will make your application "orders
of magnitude" faster.
Perhaps you could give more details.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]