Viewing the padded bytes

S

sasha

Joe Pfeiffer wrote:
[snip]
Compilers are tools like any other. A hammer is there to make life
easy for you, but your life is likely to be much more pleasant if you
don't use it to drive screws into sheet metal.

Similarly, how you code can have a large effect on how well the
compiler does its job.


I bet that thinking too much about such details today is
counter-productive. Whether I write a .something or a->something is
mostly irrelevant for the compiler - it will choose a better option
regardless.

What compiler cannot do is select a binary search instead of the bubble
search - time should be spent on such decisions, not what syntax to use
so the compiler will generate 'optimal' code.

..a
 
R

Rainer Weikusat

sasha said:
Joe Pfeiffer wrote:
[snip]
Compilers are tools like any other. A hammer is there to make life
easy for you, but your life is likely to be much more pleasant if you
don't use it to drive screws into sheet metal.
Similarly, how you code can have a large effect on how well the
compiler does its job.

I bet that thinking too much about such details today is
counter-productive.

Using languages compiled to machine code for tasks which do not
required this amount of attention to details is counterproductive
nowadays (and it has been this way for quite a while).
Whether I write a .something or a->something is
mostly irrelevant for the compiler - it will choose a better option
regardless.


'The compiler' does not exist. A compiler I happened to work with
lately happened to have problems translating index loops, despite it
should theoretically be possible to even translate them to more
efficient code (and I know that another compiler for another
architecture would have done this). Consequently, I replaced the
indexed traversal with one using pointer arithemtic (after trying a
couple of other variants).

The morale of this story is that assumptions about compiler behaviour
are worthless: Either it doesn't really matter (but then the language
choice is most certainly "suboptimal") or the actual behaviour needs
to be determined and taken into account accordingly.
What compiler cannot do is select a binary search instead of the
bubble search - time should be spent on such decisions, not what
syntax to use so the compiler will generate 'optimal' code.

To the best of my knowledge, there is no such thing as 'bubble
search' and I am really convinced that nobody should still spent any
time with something as well-researched as general sorting algorithms.
In most cases, there will be a library implementation of 'a sorting
algorithm' and that should be used where applicable. Otherwise, the
problem is usually sorting a linked list and the solution is
mergesort. But the question which general algorithm to use to
solve a particular problem and the question how this algorithm should
sensibly be implemented if it has to be implemented are actually
completely unrelated.

You are already (quite apparently) confused regarding the nature of
the problem: The difference between a.something and a->something is
not one of syntax, ie that they both 'look different', but one of
semantic: They name different operations. In particular,
a.something is defined in terms of two elementary operations, + and
->, as (a + i)->something (or (*(a + i)).something).
 
K

Kaz Kylheku

sasha said:
Joe Pfeiffer wrote:
[snip]
Compilers are tools like any other. A hammer is there to make life
easy for you, but your life is likely to be much more pleasant if you
don't use it to drive screws into sheet metal.
Similarly, how you code can have a large effect on how well the
compiler does its job.

I bet that thinking too much about such details today is
counter-productive.

Using languages compiled to machine code for tasks which do not
required this amount of attention to details is counterproductive
nowadays (and it has been this way for quite a while).

This is an oversimplification of the issue. Compiling to machine code
is not aways a form of premature optimization. It's just an implementation
strategy.

Consider the language Common Lisp. There are some implementations of it
that use interpretive evaluation for interactive input (the REPL).

Then there are some that do not have an interpreter. Even expressions
evaluated at the listener prompt are translated to machine code and run.

The user experience of interacting with either one is identical.

Either compiling or interpreting code requires the /machine/ to
implement a lot of attention to detail, because the semantics of the
programming language is detailed.

What wastes the programmer's time is when he, not the machine, is asked to pay
attention to irrelevant details and responsibilities. That can happen in an
interpreted or compiled language. It's the fault of the language, not
its implementation strategy, though the two can be linked, because language
designs can be contrived in order to make particular implementation strategies
easy to achieve.
 
P

Phil Carmody

Doug Harrison said:
A double's worth of padding is required after the char in "alternative".
Otherwise, you could not have an array of "alternative".

Why is it those who like to show off how they are 'Most Valuable
Professionals' (three times in just one post - sheesh!), have the
least valuable things to say?

Your statement is not just wrong, it's so wrong that even when
it might have been right, it's wrong.

Phil
 
K

Keith Thompson

Phil Carmody said:
Doug Harrison said:
A double's worth of padding is required after the char in "alternative".
Otherwise, you could not have an array of "alternative".
[...]

Why is it those who like to show off how they are 'Most Valuable
Professionals' (three times in just one post - sheesh!), have the
least valuable things to say?

Your statement is not just wrong, it's so wrong that even when
it might have been right, it's wrong.

It's not *that* wrong. The padding that's likely to be required
is sizeof(double)-1 bytes, and that's required only if double
requires sizeof(double) alignment. And one could quibble about
"struct alternative" vs. "alternative. But Doug's basic point is
valid -- and pointing out the errors might have been helpful.
 
S

sasha

Rainer Weikusat wrote:
[snip]
To the best of my knowledge, there is no such thing as 'bubble
search' and I am really convinced that nobody should still spent any

You are correct - my typo - I was talking about sorting algorithms.
Shows when I used bubble sort last time said:
You are already (quite apparently) confused regarding the nature of
the problem: The difference between a.something and a->something is


I don't think so. The problem is should a programmer be concerned with
some irrelevant coding detail? Even if a language police calls it
difference of semantics vs. syntax?
not one of syntax, ie that they both 'look different', but one of
semantic: They name different operations. In particular,
a.something is defined in terms of two elementary operations, + and
->, as (a + i)->something (or (*(a + i)).something).


The beauty of a good optimizer is that both will produce the optimal
(and often identical) code.
 
K

Kaz Kylheku

Why is it those who like to show off how they are 'Most Valuable
Professionals' (three times in just one post - sheesh!), have the
least valuable things to say?

How does this work?

Is there only one Most Valuable Professional in the world, or are
they all determined to have precisely the same value, so they tie
for first place?

Or don't the administrators of this program understand the semantics of
words like ``most'' and ``least''?

How many most significant bits are there in a word?

How many leftmost columns are there in an array?

Maybe we are dealing with only the n-th Most Valuable Professional
here, where n is not necessarily all that small?
 
J

jameskuyper

sasha said:
Whether I write a .something or a->something is
mostly irrelevant for the compiler - it will choose a better option
regardless.


Rainer Weikusat wrote: ....
You are already (quite apparently) confused regarding the nature of
the problem: The difference between a.something and a->something is


I don't think so. The problem is should a programmer be concerned with
some irrelevant coding detail? Even if a language police calls it
difference of semantics vs. syntax?
not one of syntax, ie that they both 'look different', but one of
semantic: They name different operations. In particular,
a.something is defined in terms of two elementary operations, + and
->, as (a + i)->something (or (*(a + i)).something).


The beauty of a good optimizer is that both will produce the optimal
(and often identical) code.


Producing identical code for a.something and (*(a+i)).something is
fully conforming, and quite normal. However, you were claiming that a
.something and a->something are equally good ways of writing the
same thing; a very different issue. Rainer was merely correctly
pointing out that those two expressions have very different semantics.
A compiler that generated identical code for both would be very
seriously non-conforming, unless it could be absolutely certain that
i==0.
 
S

sasha

jameskuyper wrote:
[snip]

Producing identical code for a.something and (*(a+i)).something is
fully conforming, and quite normal. However, you were claiming that a
[snip]

A compiler that generated identical code for both would be very
seriously non-conforming, unless it could be absolutely certain that
i==0.

Which one is it? Think of a function argument 'pointer to an array of
structures'.

..a
 
R

Rainer Weikusat

sasha said:
Rainer Weikusat wrote:
[...]
You are already (quite apparently) confused regarding the nature of
the problem: The difference between a.something and a->something
is not one of syntax, ie that they both 'look different', but one of
semantic:


[sentence restored]
I don't think so.

Well, you were confusing the accidental (different 'look', ie syntax)
with the essential (different operation) ...
The problem is should a programmer be concerned with
some irrelevant coding detail? Even if a language police calls it
difference of semantics vs. syntax?

.... and your last sentence suggests that you still do. And the problem
is not 'should one be considered with the irrelevant', that's
suggestive question and the only reasonable answer is 'no', but what
precisely is 'irrelevant' and what isn't.

The particular difference in question is code-wise not irrelevant when
using gcc 4.0.3 and compiling for a pentium4 target. Depending on the
purpose of the affected code and the time which can be spent on tuning
it, the code difference may matter more or less, but that's a
different question and it doesn't have a simple, general answer.
not one of syntax, ie that they both 'look different', but one of
semantic: They name different operations. In particular,
a.something is defined in terms of two elementary operations, + and
->, as (a + i)->something (or (*(a + i)).something).


The beauty of a good optimizer is that both will produce the optimal
(and often identical) code.


Except if 'optimal' is an attribute something created by 'the
optimizer' has by definition, which is true for the mathematical
meaning of 'optimization', but not for the bastardized use to express
'programmatic selection among a finite number of alternatives in order
to improve the numerical value of one or several quantifiable
attributes of the code which will be executed'. Just for the simple
reason that a general definition of when code should be considered
optimal isn't possible, eg "Is smaller better than faster?".

That said, since all of the expressions in my text above are defined
as being identical at the abstract machine operation level, a
compiler should certainly translate them to identical code. But not
because of 'a good optimizer', rather because expression subtree
constructed from will be identical.
 
J

James Kuyper

sasha said:
jameskuyper wrote:
[snip]

Producing identical code for a.something and (*(a+i)).something is
fully conforming, and quite normal. However, you were claiming that a


[snip]


Your snipping removed the following lines:
you were claiming that a
.something and a->something are equally good ways of writing the
same thing


Removal of those lines gives the false impression that the word "both"
in the following sentence refers to a.something and (*(a+i)).something.

In reality the word "both" in the above sentence refers to
a.something, and a->something.
Which one is it? Think of a function argument 'pointer to an array of
structures'.

.a

I have no idea what your question means. What does 'it' refer to? What
are the choices you're referring to when you ask "Which one"? What do
function arguments have to do with this?

I was indeed assuming, from context, that 'a' is a pointer to an array
of structures. a.something would therefore refer to the member named
'something' of the 'i+1'st member of that array. a->something refers to
the member named 'something' of the first element of that array; those
are two very different things, except in the special case where i==0.

This is a quite a trivial issue, one that doesn't deserve the number of
words that Rainer and I have spent pointing it out to you; but it still
seems to be the case that you don't understand what we were referring to.
 
R

Rainer Weikusat

Kaz Kylheku said:
sasha said:
Joe Pfeiffer wrote:
[snip]

Compilers are tools like any other. A hammer is there to make life
easy for you, but your life is likely to be much more pleasant if you
don't use it to drive screws into sheet metal.
Similarly, how you code can have a large effect on how well the
compiler does its job.

I bet that thinking too much about such details today is
counter-productive.

Using languages compiled to machine code for tasks which do not
required this amount of attention to details is counterproductive
nowadays (and it has been this way for quite a while).

This is an oversimplification of the issue. Compiling to machine code
is not aways a form of premature optimization. It's just an implementation
strategy.

Consider the language Common Lisp.

[...]

My use of terms was arguably imprecise and Lisp is a good example to
demonstrate that. Assuming that some Lisp implementation actually has
a compiler which can create native code[*], there are two general ways
of using it:

- run the compiler on the Lisp-program which has not been
specially annotated with information only relevant for such
a compiler: The result will be a compiled program which has
the Lisp runtime linked to it, but uses the same
general-purpose subroutines the uncompiled program would
use, too.

- annotate the (already working) Lisp-code with
compiler-relevant information, especially, with
(preferably restrictive) type-declarations: the compiler can
then either open-code certain operations (like addition) in
the same way a C-compiler usually would or at least use less
general subroutines which will execute more efficiently.

My use of 'language compiled to machine code' would be analogous to
the second way of using such a Lisp-compiler: Include lots of
meta-information about the data structures used by the program in the
code in order to achieve more efficient runtime behaviour at the
expense of an increase in development and debugging time (and likely,
also an increase in 'bugginess' of the final result). It's just that
'people' think 'C++' if they see 'language compiled to machine code' and
'f***ing mess of nested brackets' when they see 'Common Lisp' (this
is, of course, again an oversimplification).

[*] One could argue that the existence of such compilers is a
historical accident dating back to the time when computers
where generally a lot slower and less ressourceful than
nowadays. Eg, there used to be 'a Perl compiler' which was
(very) roughly equivalent to 'Lisp compilers', but it was
recently dropped from the language distribution, because it
never attracted enough interest to even make it work reliably,
vulgo: the execution speed of the interpreted bytecode was
demonstrably 'good enough'.

[...]
What wastes the programmer's time is when he, not the machine, is
asked to pay attention to irrelevant details and responsibilities.
That can happen in an interpreted or compiled language. It's the
fault of the language, not its implementation strategy,

In my opinion, its the fault of the person who chose a particular
implementation language when this choice results in time wasted on
details that wouldn't really have mattered for achieving a usable
solution to some problem.
 
S

sasha

James Kuyper wrote:
[snip]
I was indeed assuming, from context, that 'a' is a pointer to an array
of structures. a.something would therefore refer to the member named
'something' of the 'i+1'st member of that array. a->something refers to
the member named 'something' of the first element of that array; those
are two very different things, except in the special case where i==0.

This is a quite a trivial issue, one that doesn't deserve the number of
words that Rainer and I have spent pointing it out to you; but it still
seems to be the case that you don't understand what we were referring to.


It's always a great honor when someone of your stature speaks down to
me. I do appreciate it.

In case you still have interest, consider this:

void func( some_struct const * a ) {

...
for ( int i = 0; i < size; ++i ) {
do_func( a .member );
}

for ( int i; i < size; ++i, ++a ) {
do_func( a->member );
}
}

Hate to point this out to such a all-knowing person, but the pointer 'a'
may be incremented and it doesn't necessarily have to point to the first
element of an array.

I would be surprised if a decent optimizer doesn't generate identical
code for both loops.

Good day.
 
J

jameskuyper

sasha wrote:
....
It's always a great honor when someone of your stature speaks down to
me. I do appreciate it.

Don't mention it; the honor was well deserved.
In case you still have interest, consider this:

void func( some_struct const * a ) {

...
for ( int i = 0; i < size; ++i ) {
do_func( a .member );
}

for ( int i; i < size; ++i, ++a ) {
do_func( a->member );
}
}


You said "Whether I write a.something or a->something is mostly
irrelevant for the compiler, - it will choose a better option
regardless." I tried this on your example, but my compiler failed to
"choose a better option" (whatever that means):

void func( some_struct const * a ) {

...
for ( int i = 0; i < size; ++i ) {
do_func( a->member );
}

for ( int i; i < size; ++i, ++a ) {
do_func( a.member );
}
}

I conclude that "Whether I write a .something or a->something" is
in fact, very relevant for the compiler.

Whether you use array indices or pointers is irrelevant, as long as
you use them correctly, but that's not what you chose to say.
 
R

Rainer Weikusat

[...]
void func( some_struct const * a ) {

...
for ( int i = 0; i < size; ++i ) {
do_func( a .member );
}

for ( int i; i < size; ++i, ++a ) {
do_func( a->member );
}
}
[...]

I would be surprised if a decent optimizer doesn't generate identical
code for both loops.


IOW, an 'optimizer' would only be considered 'decent' by you if it
does. As I have already written at least twice: To my great surprise,
I have recently encountered a gcc version which didn't. Not for this
particular useless for-loop, but a more complicated real one, which I
orignally coded as indexed traversal and then recoded using pointer
arithmetic because the loop happened to warrant enough attention.

The bottom line is still that speculating about such issues, which
includes any attempt at contrived demonstrations, is a useless
exercise.
 
S

sasha

Rainer said:
IOW, an 'optimizer' would only be considered 'decent' by you if it
does. As I have already written at least twice: To my great surprise,

Certainly said:
I have recently encountered a gcc version which didn't. Not for this

And you happened to use one.
The bottom line is still that speculating about such issues, which
includes any attempt at contrived demonstrations, is a useless
exercise.

I agree that speculating is often useless (if not destructive - see the
current economic situation <g>).

That's why 'speculating' too much up-front and agonizing over a
particular approach (i.e. indexed vs pointer) is a waste of time. Once
it's shown the code has problems at run-time, then it's time to think
about more efficient options.


Regards,
..a
 
R

Rainer Weikusat

sasha said:
And you happened to use one.

More correctly: I happen to know that at least one compiler exists
which does not act in accordance with your beliefs about
compilers, which is sufficient to demonstrate that they are wrong.
 
B

Bo Persson

Rainer said:
More correctly: I happen to know that at least one compiler exists
which does not act in accordance with your beliefs about
compilers, which is sufficient to demonstrate that they are wrong.

I also met a few compilers in the mid 1980s that were easy to beat, on
the then current hardware.

That doesn't mean that anyone can easily write well scheduled assembly
code for today's multi-issue, multi-level-cache hardware.

Should we add multi-chip and multi-core requirements as well? :)


Bo Persson
 
S

sasha

Rainer said:
More correctly: I happen to know that at least one compiler exists
which does not act in accordance with your beliefs about
compilers, which is sufficient to demonstrate that they are wrong.

The fact you know of one compiler (for embedded HW, probably) doesn't
mean I have to spend time thinking about that it may produce sub-optimal
code.

Nowhere in what I wrote you will find a statement that all compilers
have good optimizers. What you will find is an objection that one should
be spending time 'making it easy' for the compilers.

..a
 
P

Phil Carmody

Keith Thompson said:
Phil Carmody said:
Doug Harrison said:
OTOH, with
struct alternative {
double a;
double c;
char b;
};
it is likely that no padding would be required after the char element.

A double's worth of padding is required after the char in "alternative".
Otherwise, you could not have an array of "alternative".
[...]

Why is it those who like to show off how they are 'Most Valuable
Professionals' (three times in just one post - sheesh!), have the
least valuable things to say?

Your statement is not just wrong, it's so wrong that even when
it might have been right, it's wrong.

It's not *that* wrong.

Permit me to slice and dice your response, please.

DH (MVP): "A double's worth of padding" - wrong.
KT: "is sizeof(double)-1 bytes" - correct.

DH (MVP): "is required" - wrong.
KT: "likely to be required" - correct.

DH (MVP): "after the char in "alternative"" - wrong.
KT: - didn't address the issue of endianness.

DH (MVP): "Otherwise, you could not have an array of "alternative"." - wrong.
KT: "only if double requires sizeof(double) alignment" - correct.

So the "MVP" was wrong, wrong, wrong, and wrong, unlike your good self
who was only wrong in the "It's not *that* wrong" part.

Phil
 

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,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top