Effeciency

D

David Rasmussen

That wouldn't be as effecient as an int, would it?
Oops missed the 'unsigned long int' below, then this should be

typedef std::bitset<64> MyClass;

unsigned long int is at least 32 bits according to the standard, not at
least 64 bit. Sadly, there is no standard way to get a 64-bit integer.

/David
 
J

Jeff Flinn

David Rasmussen said:
That wouldn't be as effecient as an int, would it?

That would depend on the implementation. By I would guess that 32 bits being
the most common usage, that there would be little difference. VC7.1 bitset
uses unsigned long as it's base storage type.
unsigned long int is at least 32 bits according to the standard, not at
least 64 bit. Sadly, there is no standard way to get a 64-bit integer.

Sorry I confused 'long int' with 'long long'. This is the first time I've
come across 'long int' syntax. I usually see either long or int, both of
which on the majority of todays desktop systems are 32 bits.

Jeff
 
R

Ron Natalie

David Rasmussen said:
The reason Ariane 5 crashed was because two different departments were
using ints to express the concept of length.

I understand your point, but the above is wrong. The famous Ariane crash
happened because of a overflowing conversion from 64 bit float to 16 bit
integer caused a software exception.
 
D

David Rasmussen

Ron said:
I understand your point, but the above is wrong. The famous Ariane crash
happened because of a overflowing conversion from 64 bit float to 16 bit
integer caused a software exception.

Okay :) But the point still stands.

/David
 
N

Niklas Borson

David Rasmussen said:
I want to have an unsigned integer-like thing, that for now only allows
the bitwise operators. I have defined these operators and their
self-assigning cousins.

I've done this from time to time. To reduce the amount of boilerplate
code you have to write, a class template can be helpful (see below).
This works, but I would like to know what the most effecient way of
doing this is. Preferably, there should be no overhead compared to a
read unsigned long int. No extra temporaries, full inlining etc. The
same code should be generated.

There is a term for this: abstraction penalty. That is, using a UDT
instead of a built-in type adds an abstraction (plus associated error
checking), and the question is: what performance penalty (if any) do
I pay for this abstraction.

The best advice I can give is try it and see. Make sure you compile
both versions with optimizations and then compare assembly listings
and/or time both versions.

Ideally, the performance penalty should be nil, but this may not be
true in practice. Compilers are getting better and better at this
though.

Now, as to that class template... one approach is to define a very
minimal class as follows:

template<class Representation, int TypeID>
class NewType {
Representation rep;
public:
explicit NewType(Representation const& val) : rep(val)
{}

Representation const& Value() const
{
return rep;
}
};

The second template parameter's only purpose is to enable one to
define distinct types with the same underlying representation.
For example, the following typedefs name three distinct types:

typedef NewType<double, __LINE__> Meters;
typedef NewType<double, __LINE__> Seconds;
typedef NewType<double, __LINE__> MetersPerSecond;

Of course, the above class has very minimal functionality, but
there's just enough to allow you to implement operators, etc.,
as free functions, thus:

inline Seconds operator*(Seconds lhs, double rhs)
{
return Seconds(lhs.Value() * rhs);
}

inline Meters operator*(Seconds lhs, MetersPerSecond rhs)
{
return Meters(lhs.Value() * rhs.Value());
}

Using these operators we can easily demonstrate that Meters,
Seconds, and MetersPerSecond really are distinct types.

void test(Seconds time, MetersPerSecond speed)
{
Meters distance = time * speed;
Seconds s = time * speed; // error
}
 
E

E. Robert Tisdale

Niklas said:
There is a term for this: abstraction penalty.

This mythical "abstraction penalty" is as illusive as Bigfoot.
That is, using a UDT instead of a built-in type adds an abstraction
(plus associated error checking), and the question is,
"What performance penalty (if any) do I pay for this abstraction?"

Please show us an example
where abstraction exacts a performance penalty.

Every alleged case of an "abstraction penalty" that I have seen so far
actually turns out to bad design and/or poor implementation.
I'd just like to see *one* case
where a penalty for abstraction is unavoidable so that I could believe.
 
A

Attila Feher

Jeff said:
Aha... Well, never mind then. :)


Do you know where I can get more info? (I'm not a big fan of language
extensions in general, so I'm not cyring too hard about this.) I just
tried STW, though, and couldn't find anything relevant.

If you look at discussions about this topic (there were both in comp.std.c++
and comp.lang.c++.moderated) you will see that names (sitting in teh
committee) always wipe strong typedefs off the table. The reason is that
there is no real use case for it, except maybe in a RAD kinda development.

If you look at Lois'es paper, you can see that the strong typedefs are lies.
Day is not an int. Month is not an int. And so forth. You can assing
34987590 to an int, but neither day, nor month can ever be that big.

So while under rapid development it might be beneficial to fast make
day/month etc. types without actually implementing them first, IMHO it would
only bring errors into the code, since you may forget that "lieing" type
there...

What I rather would like to see is user defined literal. So that I could
say 12day (of course this is not a proposed syntax, only an example) and the
system could check compile time if it _can_ be a day. And this leads to run
code of the sources being compiled, compile time... Also "adding new
syntax" to the language. So it is an area to be approach very carefully.
 
N

Niklas Borson

E. Robert Tisdale said:
This mythical "abstraction penalty" is as illusive as Bigfoot.


Please show us an example
where abstraction exacts a performance penalty.

See above where I said "(if any)". I never made any claims about
the value of the abstraction penalty, only that the term exists and
that it's a useful benchmark.

Google for "abstraction penalty" "stepanov benchmark" and you should
find some useful information. I believe this benchmark yields values
close to 1 for some (but not all) modern C++ compilers.
Every alleged case of an "abstraction penalty" that I have seen so far
actually turns out to bad design and/or poor implementation.
I'd just like to see *one* case
where a penalty for abstraction is unavoidable so that I could believe.

I agree with you that one should not avoid useful abstractions because of
ill-conceived assumptions about their performance. (In fact, one of the
motivations of the Stepanov benchmark was to debunk such assumptions.)

Just for fun, consider the following three nearly-identical functions.
All three multiply two doubles and return the product; the difference
is in whether some or all of the doubles are encapsulated by UDTs
(The UDTs and operator* are defined in my previous post):

double test1(double time, double speed)
{
return time * speed;
}

double test2(Seconds time, MetersPerSecond speed)
{
return (time * speed).Value();
}

Meters test3(Seconds time, MetersPerSecond speed)
{
return time * speed;
}

One possible "abstraction penalty" benchmark would be to compare
the timings of these three functions. I haven't done so, but I did
compare the assembly listings generated by MSVC7.1 at -O2. I found
that the first two functions generate identical code. The third is
slightly different because the return mechanism for UDTs differs
from that for doubles. (The double is left on the floating point
stack; the UDT is stored in a caller-specified memory address.)

Based on the above, I would expect negligable or no performance
difference between code that used the built-in double type and
code that used the more strongly-typed wrapper classes. That,
however, is a useful thing to know which is why it's useful to
have abstraction penalty benchmarks.
 
E

E. Robert Tisdale

Niklas Borson wrote:

Just for fun, consider the following three nearly-identical functions.
All three multiply two doubles and return the product; the difference
is in whether some or all of the doubles are encapsulated by UDTs
(The UDTs and operator* are defined in my previous post):
inline
double test1(double time, double speed) {
return time*speed;
}
inline
double test2(Seconds time, MetersPerSecond speed) {
return (time*speed).Value();
}
inline
Meters test3(Seconds time, MetersPerSecond speed) {
return time*speed;
}

One possible "abstraction penalty" benchmark would be to compare
the timings of these three functions. I haven't done so, but I did
compare the assembly listings generated by MSVC7.1 at -O2. I found
that the first two functions generate identical code. The third is
slightly different because the return mechanism for UDTs differs
from that for doubles. (The double is left on the floating point
stack; the UDT is stored in a caller-specified memory address.)

Based on the above, I would expect negligable or no performance
difference between code that used the built-in double type and
code that used the more strongly-typed wrapper classes.
That, however, is a useful thing to know which is why
it's useful to have abstraction penalty benchmarks.

I think that you would find that the "abstraction penalty"
would disappear in the examples above
if you would just inline these functions.

C++ languages designers and compiler developers
have gone to great pains to ensure that
there is no inherent abstraction penalty.
 
N

Niklas Borson

E. Robert Tisdale said:
Niklas said:
[snip]

Based on the above, I would expect negligable or no performance
difference between code that used the built-in double type and
code that used the more strongly-typed wrapper classes.
That, however, is a useful thing to know which is why
it's useful to have abstraction penalty benchmarks.

I think that you would find that the "abstraction penalty"
would disappear in the examples above
if you would just inline these functions.

Sure, provided the functions were actually inlined which should
be the case for trivial functions like these.

For non-inlined functions, it appears that UDTs cannot be returned
using exactly the same mechanism as native types (on this particular
implementation). However, in all other respects, the compiler
generates exactly the same code for the "more abstract" UDT as it
does for the native type.

It seems to me that where I said "negligable or no performance
difference" above covers these two cases pretty well.

I'm struggling to figure out where we disagree here.
C++ languages designers and compiler developers
have gone to great pains to ensure that
there is no inherent abstraction penalty.

True, but this is an area where (1) all compilers are not equal,
(2) a lot of progress has happened quite recently, and (3) the
existence of an "abstraction penalty" benchmark may well have
helped along some of the progress in this area.
 
D

David Rasmussen

Attila said:
If you look at discussions about this topic (there were both in comp.std.c++
and comp.lang.c++.moderated) you will see that names (sitting in teh
committee) always wipe strong typedefs off the table. The reason is that
there is no real use case for it, except maybe in a RAD kinda development.

The real use cases are everywhere. I know of almost no code that
coulnd't benefit from this. It is a very very basic design notion.
If you look at Lois'es paper, you can see that the strong typedefs are lies.
Day is not an int. Month is not an int. And so forth. You can assing
34987590 to an int, but neither day, nor month can ever be that big.

But you can't assign 34987590 to a Day. So all is fine.
So while under rapid development it might be beneficial to fast make
day/month etc. types without actually implementing them first, IMHO it would
only bring errors into the code, since you may forget that "lieing" type
there...

How? Why? What do you mean? Do you know the languages that have this,
and how natural and beneficial it is there?
What I rather would like to see is user defined literal. So that I could
say 12day (of course this is not a proposed syntax, only an example) and the
system could check compile time if it _can_ be a day. And this leads to run
code of the sources being compiled, compile time... Also "adding new
syntax" to the language. So it is an area to be approach very carefully.

I don't quite understand your proposal.

/David
 
D

David Rasmussen

Niklas said:
There is a term for this: abstraction penalty. That is, using a UDT
instead of a built-in type adds an abstraction (plus associated error
checking), and the question is: what performance penalty (if any) do
I pay for this abstraction.

Sure, but my point is that sometimes we want something with the same
semantics and the same operations as a built-in type (which usually have
great direct hardware support). So why should the language force you to
have non-zero overhead, when it is very trivial and easy, formally, to
get zero-overhead? If I define two types that are equivalent to
'double', but one represents length in feet, the other in meters, then I
want these types to behave exactly as doubles, and there is no need for
anything but a compile time penalty. But if I want the type system to
help me enforce this very basic design notion, C++ forces my to add
(possible) runtime overhead. Bjarne Stroustrup's own analogy example of
what static typing is, in TC++PL is even about physical units. He
explains how scientists are very careful not to mix different units.
This notion appears in the base of every design. That doesn't mean that
we couldn't use all of C++'s other features on top. It just strengthens
the base and the type system.
[SNIP template]

Thanks.

/David
 
A

Attila Feher

David said:
The real use cases are everywhere. I know of almost no code that
coulnd't benefit from this. It is a very very basic design notion.

I am yet to see *one* real use case for it. You did not list one either.
But you can't assign 34987590 to a Day. So all is fine.

Of course you can. Day is just another name for int.
How? Why? What do you mean? Do you know the languages that have this,
and how natural and beneficial it is there?

I see nothing natural and beneficial in calling an int Day and then still
allow it to behave as an int. Like how much sense Day << 12 makes? Or Day
& 0xabcd076f?
I don't quite understand your proposal.

It is not my proposal. It is Bjarne Stroustrup's:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1511.pdf
 
A

Attila Feher

Jonathan said:
If I recall correctly, Stroustrup's favored treatment of user-defined
literals (which I think would be very useful) does not involve new
syntax. I think he was leaning toward using ordinary constructor
invocation notation for literals, and no special keyword for literal
constructors.

That is why I said: "of course this is not a proposed syntax". It was only
an example.
 
D

David Rasmussen

Attila said:
I am yet to see *one* real use case for it. You did not list one either.

Show me some random code from whereever, and I shall show you where
strong typedefs would only improve the correspondance between the real
world notions involved, the design, and the C++ implementation.

Look at some quality Ada, ML, Pascal etc. code (it exists in abundance).
They all use this notions at the base of the language in one form or
another.
Of course you can. Day is just another name for int.

Then you don't understand the full idea of strong typedefs. Day would be
another type altogether, and you can't assign any other type to this or
vice versa. But you of course supply explicit conversions. Closely
related to strong typedefs is the possibility of controling operations
and conversions of the type used.

The obvious way of making a string Day type would not be to make it from
an int, but from an enum. If I could say

strong_typedef enum Day {monday,tuesday,[...],sunday};

you could only assign a Day to a Day, not an int. As said above, you
should be able to control the interface of such a strong type, that is,
it's operations and conversions. So you should be able to disable
operations that doesn't make sense.

In fact, there is nothing int-like about Day from a design point of view
or from the POV of the users of Day, even if an enum is represented by
the compiler as an int. It's just that the compiler already is very
effecient at using ints (and hence enums), so we base our new type on
that mostly because we need the same semantics, and we might as well
have the ease of defining this relationship, and the effeciency that
follows. It shouldn't be hard to express basic design notions.
I see nothing natural and beneficial in calling an int Day and then still
allow it to behave as an int.

It is not allowed to behave like an int. Not if the feature is
implemented correctly.
Like how much sense Day << 12 makes? Or Day
& 0xabcd076f?

It doesn't and shouldn't be allowed. One should be able to control the
interface to ones strong type, and disable unmeaningful operations.

I really can't believe you're asking questions like this since the
notion of strong typedefs and the notion of strong enums and strong
types derived from a native type, are not new in most languages. The
benefits and properties etc. of these are well known.

Well, I still didn't understand it the way you wrote it, but I will read
the proposal.

/David
 
A

Attila Feher

David said:
Show me some random code from whereever, and I shall show you where
strong typedefs would only improve the correspondance between the real
world notions involved, the design, and the C++ implementation.

Why would I work on it? You are the one who wants it.
Look at some quality Ada, ML, Pascal etc. code (it exists in
abundance). They all use this notions at the base of the language in
one form or another.

We talk about C++ here, and what matters if it makes sense in C++.
Then you don't understand the full idea of strong typedefs. Day would
be another type altogether, and you can't assign any other type to
this or vice versa. But you of course supply explicit conversions.
Closely related to strong typedefs is the possibility of controling
operations and conversions of the type used.

And it would still have all the (mostly meaningless) operations what can be
done on an int.
The obvious way of making a string Day type would not be to make it
from an int, but from an enum. If I could say

strong_typedef enum Day {monday,tuesday,[...],sunday};

That is DayOfWeek...
you could only assign a Day to a Day, not an int. As said above, you
should be able to control the interface of such a strong type, that
is, it's operations and conversions. So you should be able to disable
operations that doesn't make sense.

Except that we were not talking about days of a week, but the day part of a
date.

[SNIP]
It is not allowed to behave like an int. Not if the feature is
implemented correctly.

So how _would_ it behave? You just gave a separate name for a type.
It doesn't and shouldn't be allowed. One should be able to control the
interface to ones strong type, and disable unmeaningful operations.

So you are - in fact - creating another type, and *not* a strong typedef.
I really can't believe you're asking questions like this since the
notion of strong typedefs and the notion of strong enums and strong
types derived from a native type, are not new in most languages. The
benefits and properties etc. of these are well known.

Then name *one*. You have just proven above yourself, that you want
something else/more than strong typedef. Just to create another name for a
type is useless.

Adding more freedom to C++ to create user defined types - up to a sensible
level, keeping the zero overhead policy in mind - is a good thing to do.
Strong typedef OTOH are not.
 
D

David Rasmussen

Attila said:
I do not recall very strong doubts from that book.

Then read it again. Several places Bjarne expresses that some things are
not exactly as he wishes, and may well be changed later.
"weak defense" I guess you do not need to work anymore on self-confidence,
you can start on modesty and rhetorics.

So now I am not allowed to say anything building on my own experience
and the experience of thousand others using the features in question, in
other languages, just because it disagrees with Bjarne? Get real.

I am not at all claiming to understand language design as well as Bjarne
does. But that doesn't mean that some points in his book are more well
argued than others. For example, he sometimes say something to the
effect that this or that feature isn't included since, although it can
be helpful and powerful, it can also be misused especially by beginners.
I don't think that is a valid argument against a feature. He even says
so himself, that one shouldn't underestimate the professional
programmers that uses a language.
So why don't you? With real arguments - of course.

You want me to write a book? I am just stating my opinions after reading
the book (a year ago or so). If I were to write a useful and valid
criticism that anybody can use for anything, I am not going to say
something random in a newsgroup. I would read the book again and
carefully comment where I thought i was needed, and publish it somewhere
on the net. Many people would disagreee with me, and maybe many would
agree. You would have a hard time making a hard proof that one group was
right and the other was wrong. Language design is not an exact science.
But I haven't done this yet. That doesn't mean that I can't comment on
his book. Or if so, then please don't comment yourself.
Phew. This is reassuring.

You're hard to take serious now. Please take the discussion serious, or
don't say anything.
As I have asked you to find it, I find it interesting how it turned to be my
job anyways.

I don't see how it is my job. As far as I'm concerned, you're the one
making unsubstantiated statements. I understand that you probably think
the same about me. But that doesn't make it my job more than yours.
What I can tell you is that I was able to find convincing
arguments of why Bjarne Stroustrup decided on not allowing inheritance from
fundmental types at all.

That's not exactly what we're discussing. I am not necesarily talking
about "inheritance" in the traditional C++ way.
Therefore I find it no surprise that he did not
waste the trees (the paper) to describe why some sort of other things are
not allowed on those types.

Sorry, I don't understand this.
I do not need to.

Well, yes, if you want to participate in a discussion where that is your
point.
At the time todays C++ has been created Bjarne Stroustrup
came up with them.

And therefore we can't discuss the langauge?
OTOH if you feel that you can come up with a feasible
proposal on how to add those things into the language, and you come up with
use cases supporting the need, and you come up with convincing arguments of
why to do it the way you want it and you make an offical proposal out of it
which *convinces* *me*, I will be more than happy to present it for a straw
vote to the Evolution Working Group in Sydney this spring.

I don't have the time, but it is an interesting proposal. I will
consider writing a proposal whenever I get the time to do it.
But whatever you
propose has to stand the critical eyes of compiler implementers as well as
the finest minds of the industry (I am not talking about me). So be careful
to make it very convincing and possible to implement.

Of course. "Possible to implement" is the easy part. It is a feature
that is so easy to add to any C++ compiler implementation in existance,
in my experience with compiler design.
As for the issue itself. My feeling is that it is possible to come up with
a library based solution, which can be feasible on any quality
implementation.

Really? Interesting. Could you outline this?
So based on that I find it hard to believe that anyone can
justify a core language change - but unlike you, my mind is not closed by a
prejudical decision.

It seems like you are the prejudical one right now. You don't know
anything about whether my "mind is closed". I don't know whether yours
is, which is why I don't make such unsubstantiated claims, that btw. has
nothing to do with the subject at hand. Let's get back to that.
Show what you have got, where and how would you change
the language/the standard, what would it effect and why, what use cases
makes it possible etc. I am sure it is not only me, who will be ready to
look at it.

This issue is important to me so I probably will do that, when time
allows it.

/David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,196
Latest member
ScottChare

Latest Threads

Top