Using printf in C++

I

Ian Collins

Look, I am a stupid C programmer and you are a smart C++ programmer, you
are right and I am wrong.

inlining functions reduces code size, and C++ is the best thing
since invented since sliced bread

Why don't you address the specific points and in my case, real world
examples we have presented rather than chucking your toys out of your cot?

You implied I was being dishonest, the least you can do is address the
points in the rebuttal I posted.
 
J

jacob navia

Le 20/05/12 23:03, Ian Collins a écrit :
You implied I was being dishonest, the least you can do is address the
points in the rebuttal I posted.

You are not dishonest. It is just that in some very special cases like
the ones you showed, yes, inlining *can* be shorter. In the
overwhelming majority of others cases, inlining increases code
size provoking code bloat. This is an accepted truth in all
compiler circles, and the necessity of avoiding code bloat
when inlining has a LONG history in the compiler literature
about how to best avoid it, heuristics to use, etc etc.

For instance in one of your examples the occurrence of
an immediate constant allowed inlining to erase some statements.
Fine. But in the normal case, the situation is not like that
and you have a variable that you can't plug in as a constant.

But I do not feel like doing a class in compilers and inlining here.

I just want to show you this:

~/ccl $ g++ -c -Os list.c
~/ccl $ ls -l list.o
-rw-r--r-- 1 jacobnavia staff 38752 20 mai 23:46 list.o
~/ccl $ gcc -c -Os list.c
~/ccl $ ls -l list.o
-rw-r--r-- 1 jacobnavia staff 36236 20 mai 23:47 list.o
~/ccl $ wc list.c
2040 5823 54908 list.c
~/ccl $

It is not just inlining since I haven't got a single "inline" declaration.

C++bloats the code for MANY reasons and one of the reasons is that
C++ programmers believe that inlining is OK.
 
I

Ian Collins

Le 20/05/12 23:03, Ian Collins a écrit :


You are not dishonest. It is just that in some very special cases like
the ones you showed, yes, inlining *can* be shorter. In the
overwhelming majority of others cases, inlining increases code
size provoking code bloat. This is an accepted truth in all
compiler circles, and the necessity of avoiding code bloat
when inlining has a LONG history in the compiler literature
about how to best avoid it, heuristics to use, etc etc.

Very true, which is why dedicated embedded compilers give the programmer
fine control over what should be inline.

As for "some very special cases", I'd say these are in fact very common
cases. Just look at the standard library code, there you will find a
great many trivial functions. std::vector couldn't match the space/time
performance of a raw array without them.

Because C++ has had "inline" longer than C, idiomatic C++ code tends to
make greater use of trivial functions that will be inlined away. Yes of
course inappropriate inlineing can cause bloat, but the situation is no
worse, better in fact, the the heavy use of macros in some C code. With
macros, the choice of what to inline has to be made by the programmer
when the code is written. With inline, the choice is postponed until
compile time and can be delegated to the compiler in most situations.
This is really important when code is to be compiled for a variety of
targets with differing cache and memory sizes.

So I content that a more appropriate statement is macros increase code
bloat.
For instance in one of your examples the occurrence of
an immediate constant allowed inlining to erase some statements.
Fine. But in the normal case, the situation is not like that
and you have a variable that you can't plug in as a constant.

I know, that's why I mentioned that situation in this sub-thread. the
compiler can include the code for the inlined function in its
optimisations, including register allocations.
But I do not feel like doing a class in compilers and inlining here.

Being patronising doesn't advance your cause.
I just want to show you this:

~/ccl $ g++ -c -Os list.c
~/ccl $ ls -l list.o
-rw-r--r-- 1 jacobnavia staff 38752 20 mai 23:46 list.o
~/ccl $ gcc -c -Os list.c
~/ccl $ ls -l list.o
-rw-r--r-- 1 jacobnavia staff 36236 20 mai 23:47 list.o
~/ccl $ wc list.c
2040 5823 54908 list.c
~/ccl $

It is not just inlining since I haven't got a single "inline" declaration.

I'm sure I don't have to tell you that inline is only a hint.

What is list.c?
C++bloats the code for MANY reasons and one of the reasons is that
C++ programmers believe that inlining is OK.

In every case I've checked, g++ produces the same optimised code as gcc
for the same source. I think it even invokes gcc to compile a .c source
file.

I would be very happy to be proved wrong, so please provide some example
source.
 
J

jacob navia

Le 21/05/12 00:29, Ian Collins a écrit :
I'm sure I don't have to tell you that inline is only a hint.

What is list.c?

Is the list container, where I changed two things:

1) automatic casts from void * were eliminated
2) const correctness was forced in the few functions that
accept literal character strings
3) Substituted bool by int everywhere so that it doesn't
conflict with the C++ built-in type "bool".

Then I compiled both with g++ and with gcc. The result is
a huge 2K difference.

I will upload the source code into the ccl directory
next week and you will be able to test it yourself.
 
M

MelissA

Le 21/05/12 00:29, Ian Collins a écrit :

Is the list container, where I changed two things:

1) automatic casts from void * were eliminated
2) const correctness was forced in the few functions that
accept literal character strings
3) Substituted bool by int everywhere so that it doesn't
conflict with the C++ built-in type "bool".

Then I compiled both with g++ and with gcc. The result is
a huge 2K difference.

I will upload the source code into the ccl directory
next week and you will be able to test it yourself.

Have you tried -fno-exceptions flag when compiling as c++ ?
 
J

jacob navia

Le 21/05/12 01:03, MelissA a écrit :
Have you tried -fno-exceptions flag when compiling as c++ ?

~/ccl $ g++ -fno-exceptions -Os -c list.c
~/ccl $ ls -l list.o
-rw-r--r-- 1 jacobnavia staff 38752 21 mai 01:10 list.o
~/ccl $ g++ -Os -c list.c
~/ccl $ ls -l list.o
-rw-r--r-- 1 jacobnavia staff 38752 21 mai 01:10 list.o
~/ccl $
 
I

Ian Collins

On 05/21/12 10:39 AM, jacob navia wrote:

Then I compiled both with g++ and with gcc. The result is
a huge 2K difference.

I will upload the source code into the ccl directory
next week and you will be able to test it yourself.

Didn't you have anything to say to the main part of my reply?
 
J

jacob navia

Le 21/05/12 01:03, MelissA a écrit :
Have you tried -fno-exceptions flag when compiling as c++ ?

You can download the source code of the ccl from here

http://code.google.com/p/ccl/

The file to compile is "list.c"

just unpack ccl.zip, and type the commands I wrote.
Please tell me if you obtain the same results

jacob
 
J

jacob navia

Le 21/05/12 01:13, Ian Collins a écrit :
On 05/21/12 10:39 AM, jacob navia wrote:



Didn't you have anything to say to the main part of my reply?

You assume perfect worlds. Actually C++ *mandates* that functions
defined in the class definition must be inlined...

At the start those functions start with one liners, but later on they
bloat. And then nobody takes them OUT of the class definition
because nobody cares about those "low level details".

It is the whole attitude about caring about code size, performance,
and all those related issues that disappears.

Yes, in the cases that you mention inline is useful. I have even
implemented inline in lcc-win compiler. But in the C++ code that
I am payed to maintain inline is a catastrophe.
 
M

MelissA

Le 21/05/12 01:03, MelissA a écrit :

You can download the source code of the ccl from here

http://code.google.com/p/ccl/

The file to compile is "list.c"

just unpack ccl.zip, and type the commands I wrote.
Please tell me if you obtain the same results

jacob

Results are same as yours. gcc 4.6.3.


bmaxa@maxa:~/jacob/ccl$ g++ -Wall -c -Os -fno-exceptions list.c
bmaxa@maxa:~/jacob/ccl$ ls -l list.o
-rw-rw-r-- 1 bmaxa bmaxa 27104 May 21 01:36 list.o
bmaxa@maxa:~/jacob/ccl$ g++ -Wall -c -Os list.c
bmaxa@maxa:~/jacob/ccl$ ls -l list.o
-rw-rw-r-- 1 bmaxa bmaxa 27104 May 21 01:36 list.o
bmaxa@maxa:~/jacob/ccl$ gcc -Wall -c -Os list.c
bmaxa@maxa:~/jacob/ccl$ ls -l list.o
-rw-rw-r-- 1 bmaxa bmaxa 25808 May 21 01:36 list.o
bmaxa@maxa:~/jacob/ccl$ g++ -m32 -Wall -c -Os -fno-exceptions list.c
bmaxa@maxa:~/jacob/ccl$ ls -l list.o
-rw-rw-r-- 1 bmaxa bmaxa 22440 May 21 01:37 list.o
bmaxa@maxa:~/jacob/ccl$ g++ -m32 -Wall -c -Os list.c
bmaxa@maxa:~/jacob/ccl$ ls -l list.o
-rw-rw-r-- 1 bmaxa bmaxa 22440 May 21 01:38 list.o
bmaxa@maxa:~/jacob/ccl$ gcc -m32 -Wall -c -Os list.c
bmaxa@maxa:~/jacob/ccl$ ls -l list.o
-rw-rw-r-- 1 bmaxa bmaxa 21108 May 21 01:38 list.o
 
I

Ian Collins

Le 21/05/12 01:13, Ian Collins a écrit :

You assume perfect worlds.

I can't, I live and code in the real (often embedded) one!
Actually C++ *mandates* that functions
defined in the class definition must be inlined...

Er, no it does not. A function defined with a class definition is no
different from any other inline function, see 7.1.2, the pertinent
sections are:

2 A function declaration (8.3.5, 9.3, 11.3) with an inline specifier
declares an inline function. The inline specifier indicates to the
implementation that inline substitution of the function body at the
point of call is to be preferred to the usual function call
mechanism. An implementation is not required to perform this inline
substitution at the point of call; however, even if this inline
substitution is omitted, the other rules for inline functions defined
by 7.1.2 shall still be respected.

3 A function defined within a class definition is an inline function.
At the start those functions start with one liners, but later on they
bloat. And then nobody takes them OUT of the class definition
because nobody cares about those "low level details".

The compiler (unless it is totally naff) will stop inlining them.
It is the whole attitude about caring about code size, performance,
and all those related issues that disappears.

Not for us embedded developers it doesn't. it can't.
Yes, in the cases that you mention inline is useful. I have even
implemented inline in lcc-win compiler. But in the C++ code that
I am payed to maintain inline is a catastrophe.

Inline, or the compiler??
 
L

Luca Risolia

Le 21/05/12 00:29, Ian Collins a écrit :
Then I compiled both with g++ and with gcc. The result is
a huge 2K difference.

For a more "honest" comparison you should strip the symbols after the
objects have been generated:

$ ~/gcc47/bin/g++-4.7 -Wall -c -Os list.c ; strip list.o; ls -l list.o
-rw-rw-r-- 1 luca luca 14080 mag 21 00:29 list.o

$ ~/gcc47/bin/gcc-4.7 -Wall -c -Os list.c ; strip list.o; ls -l list.o
-rw-rw-r-- 1 luca luca 14080 mag 21 00:29 list.o

the size is the same.
 
I

Ian Collins

You can download the source code of the ccl from here

http://code.google.com/p/ccl/

The file to compile is "list.c"

just unpack ccl.zip, and type the commands I wrote.
Please tell me if you obtain the same results

I don't have the time to examine the outputs in detail, but a quick
glance (backed up be slapping extern "C" around the code to suppress
mangling of names) shows that the generated function code is pretty much
identical. The extra size is mainly symbolic information which is more
complex for C++.

If you generate assembly and diff the results, you will see the
differences are overwhelmingly symbolic.
 
P

Pavel

Ian said:
iostreams aren't an example of comparing an exception based solution with a call
and test solution.
This was not the OP's issue.
Try a more apples to apples comparison and exceptions will
win the day.
I believe it is an apple-to-apple comparison to compare performance (in code
speed and/or size) of two functionally roughly equivalent subsets of two I/O
libraries (C printf vs C++ streams+std::string), one designed to report errors
via exceptions (among the others) and another using errno facility for the same.

Now that I thought of it more, the case seems to be deserving deeper analysis (I
myself was a moderate proponent of exceptions before and now I am having a
second thought). This is because the exception-based method of error reporting
seems to break one of the declared principles of C++ design: you only pay for
the features you use. From this viewpoint, this is what concerns me:

Most of the applications of i/o library and std::string, expect errors (those
reported by exceptions) to occur extremely rarely. Especially, with std::string
the "right thing to do" seems to be to ensure in advance (by means of data
checking at coding stage and providing adequate memory at system management
stage) that errors reported by exceptions *never* occur. At the same time, a
call to every function using std::string and/or C++ stream APIs pays price for
this extremely rarely used facility. This breaks one of the basic principles of
design-for-performance I follow (which is to ensure that the identified
"likely-hit" code paths run as fast as possible).

BTW from the above perspective the recently posted questions asking help for
manipulation with C-style strings seem to have some practical application.

-Pavel
 
P

Pavel

Martin said:
Are you trying to make a point wrt. templates, or wrt. the original topic
else-thread?

cheers,
Martin
I just posted a ""practical example" of this mythical "code bloat"" asked for by
the author of the post I replied to. Then, as while doing the work necessary to
measure the code size I found something else that I believed code be of interest
of the group, I shared this information as a sideline. Is this a problem?

-Pavel
 
I

Ian Collins

This was not the OP's issue.
Try a more apples to apples comparison and exceptions will
I believe it is an apple-to-apple comparison to compare performance (in code
speed and/or size) of two functionally roughly equivalent subsets of two I/O
libraries (C printf vs C++ streams+std::string), one designed to report errors
via exceptions (among the others) and another using errno facility for the same.

But the differences between the two have little, if anything, to do with
exceptions. Try comparing the two approaches with and without
exceptions enabled. I doubt you will see any real difference.
Now that I thought of it more, the case seems to be deserving deeper analysis (I
myself was a moderate proponent of exceptions before and now I am having a
second thought). This is because the exception-based method of error reporting
seems to break one of the declared principles of C++ design: you only pay for
the features you use. From this viewpoint, this is what concerns me:

Not really, any decent exception implementation has no or minuscule
impact on performance unless an exception is thrown. If your code does
not use exceptions, you can always turn them off at compile time.
Most of the applications of i/o library and std::string, expect errors (those
reported by exceptions) to occur extremely rarely. Especially, with std::string
the "right thing to do" seems to be to ensure in advance (by means of data
checking at coding stage and providing adequate memory at system management
stage) that errors reported by exceptions *never* occur. At the same time, a
call to every function using std::string and/or C++ stream APIs pays price for
this extremely rarely used facility. This breaks one of the basic principles of
design-for-performance I follow (which is to ensure that the identified
"likely-hit" code paths run as fast as possible).

The price, if any, of exceptions is well worth paying compared to
manually checking any operation that would otherwise throw an exception.
Imagine having to check for success each time you create a string.

I have been working in kernel land recently and the lack of exception
support was a real pain in an environment where resource allocation is
common (and failure more likely than in user land). Even standard
idioms such as RAII become troublesome.
 
P

Pavel

Martin said:
Just a friendly poke, please don't take too seriously: I hope you are
writing this post using an operating system unknown to me that is at
least a decade old but is not fully composed of such completely horrible
and unsafe code...

Makes me wonder ... according to [The Programming Languages
Beacon](http://www.lextrait.com/vincent/implementations.html) MS Windows uses
C++ (whatever that means, it's not like we have too many details here). That
means we cannot rule out that some OS components are actually using templates.
More seriously, in my experience template code carries more bugs and
uglier than plain-old C-like code. I found it to be related to its
inherent inflexibility that manifests itself after a while, e.g. as
follows:

Well, my (limited) experience is the opposite.
1. At one moment in time we decide to use compile-time parametrization
for a new system. We assume the arguments will always be known at
compile time. We build the system and boast how flexible (...)

2. Sooner or later at least one "disruptive requirement" comes that
translates to a necessity to select an algo or type based on information
only available at run time. (...)

3. If the system is good for anything, that one disruptive requirement
is followed by a number of others, not necessarily disruptive in
themselves, but now they need to be implemented on top of over-bloated
and/or ugly hacked system. (...)

4. A 1-year-ago "beautiful little system" is now known as "an ugly
unmaintainable monster (...) Oh, yes, and compiling our system now takes
many hours on the best available hardware (maybe as long as it used to
take to add a new feature at stage #1).

I hate to say it but the initial decision to rely on compile-time
parametrization played a significant role in this outcome.

Is this FUD
Hey, it used to be my headache. You did not have to add insult to injury.

based on any actual experience or did you make it up on the spot?
Believe me, I wouldn't bother to make up ugly things like that on the spot or
otherwise. From the inside, I am all white and fluffy and prefer elegance, math,
ikebana and nirvana.
(And do you really believe that templates played a *significant* role?)
Templates are just a technique that can be used, misused and even used
maliciously. The decision to use them for the system that served as a prototype
for my narrative played main role.
Just sayin' ... you *don't have to* base the whole design of something on
templates.
Certainly. I am trying to make a stronger point though: that more often than not
you *shall not* based it (whole design of something) on templates.

You don't have to understand
[Boost.Spirit](http://boost-spirit.com/home/) to be using templates.
In fact, I believe that it may be safer (for the organization that employs you)
if you do not understand Boost.Spirit if only your lack of understanding can
keep you from "basing the whole design of something on templates" (because
understanding Boost.Spirit in particular and template meta-programming in
general seems to be provoking people to "base the whole design of something on
templates" without giving a second thought to what the problem in hands may
become at version 3.14).
std::vector and friends is just fine, stick to the basics.


To repeat, this does seem like a prudent approach, but this doesn't imply to not
use *any* templates in a big project, and your quote from above:


now more sounds like "huh, templates aren't the solution for everything, you
know", which I think noone really claims.
No, my point is that template solutions are often less pliant to change than the
non-template ones. The problem is not that you cannot write needed program with
templates. The problem is that you have written it with templates and then you
changed it few times based on your templates as was required and then you cannot
further change it based on your templates as required.
cheers,
Martin

HTH,
Pavel
 
L

Luca Risolia

Now that I thought of it more, the case seems to be deserving deeper
analysis (I myself was a moderate proponent of exceptions before and now
I am having a second thought). This is because the exception-based
method of error reporting seems to break one of the declared principles
of C++ design: you only pay for the features you use. From this
viewpoint, this is what concerns me:

There are some cases in which error reporting via exceptions is
essential to make the code *faster* than non-exception-based
alternatives, for example:

while (i < N) {
__try {
for (; i < N; i++)
a = log(b * c); // floating point math
} __catch(exCode() == OVERFLOW) { // overflow ex. from the hw
a = log(b) + log(c); // redo without overflow
++i;
}
}

There the time to make support for the exception handling is negligible,
since there is only one function call in the critical loop. The
exception is costly, when it occurs, but we assume that the occurrence
is rare.
Alternatives to look for errors are less efficient, for example checking
for overflow inside the loop requires two floating point comparisons,
which make the solution relatively costly; while using log(a) +
log(b) directly would double the number of calls to log().
 
I

Ian Collins

There are some cases in which error reporting via exceptions is
essential to make the code *faster* than non-exception-based
alternatives, for example:

while (i< N) {
__try {

Eh? What's __try?

I think you are mixing windows managed stuff with C++ exceptions.
 
P

Pavel

Ian said:
But the differences between the two have little, if anything, to do with
exceptions. Try comparing the two approaches with and without exceptions
enabled. I doubt you will see any real difference.


Not really, any decent exception implementation has no or minuscule impact on
performance unless an exception is thrown. If your code does not use exceptions,
you can always turn them off at compile time.


The price, if any, of exceptions is well worth paying compared to manually
checking any operation that would otherwise throw an exception. Imagine having
to check for success each time you create a string.
Look, the code snippets above doe not check for errors at all. For code like
this, (such as s[n]printf to an automatically allocated buffer or C++ output of
few hundreds bytes), I think it is typical, and correct. This code pays price
for exceptions those.
I have been working in kernel land recently and the lack of exception support
was a real pain in an environment where resource allocation is common (and
failure more likely than in user land). Even standard idioms such as RAII become
troublesome.
Well, let me give you an example of Symbian (proudly listed by the [The
Programming Languages
Beacon](http://www.lextrait.com/vincent/implementations.html) cited above by
Martin B. as written in C++).

Being familiar with the system quite intimately in the past, I can attest that,
true, Symbian (originally, EPOC32) was a rare example of an operating system
(very well-performing at that), almost entirely written in C++, BUT:

They did not use standard C++ exceptions in the OS. At that, they badly wanted
to use RAII (and, consequently, exceptions) and they achieved their noble goal
by creating (quite an ugly) way requiring client code cooperation via so called
"clean-up stack". That is, for the objects that you wanted to be RAII and
exception-safe, you would create an object with a (throwing, if I recall it
correctly) operator new in dynamic memory and manually put it onto so called
"cleanup stack". In case your code did not throw, you would need to clean up
your cleanup stack manually; otherwise, runtime would take care of it. You could
NOT create an exception-safe "auto" RAII object -- quite a divergence from the
standard C++ RAII implementation practice, isn't it?

(They did not use templates either but I am not sure about the reason for that
-- probably the templates were not mature enough yet in a low-common-denominator
of MSVC (that they used to build emulator binaries) and g++ (that they used to
build production binaries) -- but exceptions certainly were)

Their excuse for the "non-standard exception" mess? -- performance concerns for
using standard C++ exceptions. I tend to believe them because the guys obviously
knew everything there is to know about the design-for-performance and then some
(I am judging by the end result of their effort: at ~1998 they were selling
products built on a rock-solid hard real-time system with preemptive
multitasking, extensive support for priorities, order-of-tens-microsecond
response times for app communicating via serial and IRDA ports and perfectly
responsive nice half-VGA-size pen-interface GUI -- all powered by a poor little
7-MHz ARM CPU. I have yet to see a better-performing RTOS -- and uglier RTOS API).

-Pavel
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,007
Latest member
obedient dusk

Latest Threads

Top