Oozing poison

R

Rui Maciel

Jorgen said:
That's what the LKLM FAQ maintainer wrote. When Linus writes about
C++, he sounds almost exactly like "AD". That's probably the the first
thing that prevents C++ in the Linux kernel.

You can read for yourself what Linus Torvalds had to say about C++:

http://lkml.org/lkml/2004/1/20/20


And again, it was about using C++ on kernel code, not C++ in general.


Rui Maciel
 
N

Nick Keighley

Programming in C++ code costs more to maintain.

I've seen some pretty nasty C
Maybe this is good for offering
more programming positions for the employees. Also problems in C are inherited
in C++ such as the non-closure integer arithmetics and the lousy sort trap

the what?
and the famous non-checking for array boundaries in any read/write.
The vetctor part just covers up some traps but it slows down as expected.

what language avoids this? If you do array bound checking it costs. If
you don't then you run a risk. Are there languages that can prove
array bounds won't be breeched?
The operator reloading and the copy constructor  part of derived classes introduce
more problems  in any operation that needs a new object in the RHS to be
assigned to the LHS.

could you expand on that; I didn't quite follow you. I don't recall
encountering a problem in this area.
Of course there are people using C++ without deriving
new classses and just use those libraries in trivial programs.
Then in this cas C++ is easy to mantain.

....and isn't really C++
 
N

Nick Keighley

Am I feeding a troll now? :S

You can write perfectly robust code in C++ if you don't use pointers at all.
There's usually no need to do this, as other languages don't even have them.

how do I build complex structures? Eg.trees. How do I do runtime
polymorphism?
Use references if you pass byref, and use containers if you want to allocate
to be safe.

Containers of pointers?
 
J

Juha Nieminen

Rui Maciel said:
<quote>
My personal view is that C++ has its merits, and makes object-oriented
programming easier. However, it is a more complex language and is less
mature than C. The greatest danger with C++ is in fact its power. It seduces
the programmer, making it much easier to write bloatware. The kernel is a
critical piece of code, and must be lean and fast. We cannot afford bloat. I
think it is fair to say that it takes more skill to write efficient C++ code
than C code. Not every contributer to the linux kernel is an uber-guru, and
thus will not know the various tricks and traps for producing efficient C++
code.
</quote>

One of the most common misconceptions among advocate C hackers is that
programming in C somehow automatically makes your program more efficient
and have "less bloat" than, for example, when programming C++, which
requires more care to be as efficient and small. (And I'm discounting
here the bunch that believes that C++ *always* produces less efficient
and larger code than C.)

Linus Torvalds, in addition to that, also has this completely weird
notion that programming in C also automatically makes your code easier
to understand and maintain (while C++ is the exact opposite).

Of course practical examples tell a completely different story. There
are plenty of C programs out there that are absolutely horrendous,
inefficient, and almost completely impossible to maintain. And many of
these are not just some obscure one-man projects, but large and popular
ones. (For example much of mplayer was like this last time I checked.
Granted, this was many years ago, so it's theoretically possible that
they have done a complete refactoring of the entire code. I find it
unlikely, though.)

That's not to say that you cannot create horrendous programs in C++.
Of course you can. And probably over half of the C++ programs out there
are horrendous. OTOH, also probably over half of C programs out there
are likewise horrendous.

The difference is, however, that C++ offers you tools that C doesn't,
tools to make your code safer and easier to understand and maintain, while
still being as efficient as an equivalent C program would.
 
C

Cholo Lennon

The readability of C++ seems to get worse with each new version of the
standard. Once the<XXX> elements (static/dynamic casts, templates) were added
to the language, it became worse than perl for readability.

Really? Do you think C++ cast operators go against readability? For me
is the opposite: At glance, for example, they give you more information
than the traditional C cast and, as a result, they are (IMHO) more readable.
 
J

Jorgen Grahn

I think they may provide better _maintainability_ at the expense of
_readability_.

Keep in mind, both of you, that the best way to deal with casting is
to avoid doing it! Or isolating it to a few places. C++ provides
better tools for that than C.

....
Templates and exceptions on the other hand, have a distinct impact
on code size (thus icache footprint, thus performance).

I can't comment on exceptions, but how do templates, in general,
increase code size? Note that templates do not imply function
inlining.

/Jorgen
 
B

Brian Drummond

I've seen some pretty nasty C

They are both pretty bad.
what language avoids this? If you do array bound checking it costs. If
you don't then you run a risk. Are there languages that can prove array
bounds won't be breeched?

There certainly are.
But array bound checks rarely cost anything. If your compiler can't prove
and eliminate the bulk of them at compile time, get a better compiler.
Help it by defining a new integer type restricted to the size of the
array (even where the array size is only known at runtime) and using that
type for the index.
The remaining few checks cost, but you would have to write the same
checks by hand in C, so they don't cost extra, they just save programmer
time.

Anyone with even a little sympathy for "AD"'s position, or interest in
writing readable, maintainable, safe code would find it worthwhile to try
Ada 2005. It's available in any decent gcc installation.

Where you really need to eliminate all run time checks and prove all
bounds at or before compile time, use SPARK instead.

This is off topic on c.l.c++ but you did ask.

- Brian
 
M

MikeWhy

Brian Drummond said:
They are both pretty bad.


There certainly are.
But array bound checks rarely cost anything. If your compiler can't prove
and eliminate the bulk of them at compile time, get a better compiler.
Help it by defining a new integer type restricted to the size of the
array (even where the array size is only known at runtime) and using that
type for the index.
The remaining few checks cost, but you would have to write the same
checks by hand in C, so they don't cost extra, they just save programmer
time.

template <typename DataT, size_t LenT>
void Foo(DataT (& foo)[LenT])
{
...
}
Anyone with even a little sympathy for "AD"'s position, or interest in
writing readable, maintainable, safe code would find it worthwhile to try
Ada 2005.

Surely, this is even more absurd than the OP claim. ;)
 
I

Ian Collins

Buenas,

I think they may provide better _maintainability_ at the expense of _readability_.

It may also be that for someone coming from a C background (starting in 1978),
the dynamic/static cast syntax just looks alien and out of place.

Templates and exceptions on the other hand, have a distinct impact on code size
(thus icache footprint, thus performance).

The only performance impact I have ever measured from the use of
exceptions is improvement. I have been testing compilers since the mid
90s and I have only found one compiler/platform (that compiler refused
to inline any function that could throw) where exceptions had a
detrimental impact on performance.

In overall performance terms, templates are no worse and often a
significant improvement over the direct C equivalent (macros) because
they aren't necessary inlined. They can also provide significant
performance improvements over another C alternative, the use of void*
and function pointers (sorting being the classic example).

Yes you can end up with multiple instances of class methods, but that is
the price you pay for type safety. Anyway, what proportion of a decent
sized project would that represent? For containers of pointers one can
easily use inline wrappers round a container of void* to avoid bloat
while retaining type safety. In most code I have seen, containers of T*
are more numerous than containers of T

Thus with contemporary tools, the twin myths of exception performance
and template code bloat are just that: myths.
 
M

Miles Bader

Ian Collins said:
The only performance impact I have ever measured from the use of
exceptions is improvement. I have been testing compilers since the mid
90s and I have only found one compiler/platform (that compiler refused
to inline any function that could throw) where exceptions had a
detrimental impact on performance.

Yeah, generally exceptions are very cheap these days, probably cheaper
than equivalently-safe explicit-return-value-checking code. I guess
some of the people decrying them may be remembering some of the
initial naive implementations using setjmp/longjmp (OK, I guess the
exception handling in MS's 32-bit ABI is still kinda of stinky).
Yes you can end up with multiple instances of class methods, but that is
the price you pay for type safety. Anyway, what proportion of a decent
sized project would that represent? For containers of pointers one can
easily use inline wrappers round a container of void* to avoid bloat
while retaining type safety. In most code I have seen, containers of T*
are more numerous than containers of T

Also note that for really equivalent template expansions, one can
sometimes merge the resulting redundant functions at link-time.
In gcc, for instance, using "-ffunction-sections" at compile-time, and
"-Wl,--icf=safe" at link-time; I'm pretty sure MS's compilers have
something equivalent.

-Miles
 
I

Ian Collins

Unless you are writing an operating system or hypervisor where every byte
counts.

A large proportion of my work is in embedded devices, where the byte
count is even more important!

These days of low cost, plentiful RAM, the most important factor in
hypervisor or operating system is performance, not code size.

As I said above, if code size is an an issue, there are well known
coding practices that can be employed to minimise it.

There are also techniques used by embedded tool chains to minimise code
size. However more often than not these techniques are detrimental to
performance and should only be employed when physical resources are tight.
 
I

Ian Collins

Miles Bader said:
Yeah, generally exceptions are very cheap these days, probably cheaper
than equivalently-safe explicit-return-value-checking code. I guess
some of the people decrying them may be remembering some of the
initial naive implementations using setjmp/longjmp (OK, I guess the
exception handling in MS's 32-bit ABI is still kinda of stinky).


I tried substituting a 'new uint8[xx}' call to replace a malloc with a try
catch block for bad_alloc. I then disassembled the code. One instruction
(to test the return value of the malloc) turned into:

How comes one version has multiple calls to new and the other one call
to malloc?

What was the original source?
 
C

Cholo Lennon

Keep in mind, both of you, that the best way to deal with casting is
to avoid doing it! Or isolating it to a few places. C++ provides
better tools for that than C.

I agree, of course, but when you have to deal with legacy C or low level
code casting appears (and I have to say that there is a lot of legacy C
code)
 
I

Ian Collins

Ian Collins said:
I tried substituting a 'new uint8[xx}' call to replace a malloc with a try
catch block for bad_alloc. I then disassembled the code. One instruction
(to test the return value of the malloc) turned into:

How comes one version has multiple calls to new and the other one call
to malloc?

What was the original source?

uint8 cmd = iocb->get_op_var2();

buf = (uint8 *)malloc(bufsize);
if (buf == NULL) {
iocb->set_rd(IOT_WITH_EXCEPTIONS, RD1_OCS_MPU_PARITY);
return false;
}

switch (cmd) {

vs.

uint8 cmd = iocb->get_op_var2();

try {
buf = new uint8[bufsize];
} catch (std::exception e) {

never catch exceptions by value, always catch by const reference to
avoid slicing.
iocb->set_rd(IOT_WITH_EXCEPTIONS, RD1_OCS_MPU_PARITY);
return false;
}

switch (cmd) {

There is only one call to operator new (at 732a), the source code
is intermixed with the assembler by the 'objdump' utility and it replicated the source
line in multiple places.

This is fairly atypical use of exceptions, mixing exceptions and return
codes. It is more common to catch the exception at a higher call. This
simplifies the source and machine code (fewer conditional branches).

The catch is also the reason for all the extra code, constructing and
destructing a temporary std::exception object. The actual exception
handling part of the code is this bit:

} catch (std::exception x) {
73a0: 48 89 e7 mov %rsp,%rdi
73a3: e8 30 e1 ff ff callq 54d8
<std::exception::~exception()@plt>
73a8: e8 7b dd ff ff callq 5128 <__cxa_end_catch@plt>
73ad: 0f 1f 00 nopl (%rax)
73b0: e9 41 01 00 00 jmpq 74f6
<c_uniline_dlp::echo(c_iocb*)+0x208>
73b5: 48 89 c5 mov %rax,%rbp
73b8: 48 89 e7 mov %rsp,%rdi
73bb: e8 18 e1 ff ff callq 54d8
<std::exception::~exception()@plt>
73c0: e8 63 dd ff ff callq 5128 <__cxa_end_catch@plt>
73c5: 48 89 ef mov %rbp,%rdi
73c8: e8 3b de ff ff callq 5208 <_Unwind_Resume@plt>
return false;
}

hardly bloat! Now if the exception is caught higher up the call chain
(with f() being a void function), there would be less, rather than more
code.
 
M

Miles Bader

Yet creating and destroying a temporary std::exception object also counts in
terms of both extra cycles and code footprint. Both of which impact
performance (generally negatively).

Er, exceptions are intended to be _exceptional_.

If some exception occurs so often that it has a measurable performance
impact, it probably shouldn't be an exception.

If you look at the code generated for the two styles, one of the big
differences with the exception-using code is that the _normal_ case
(where no exception is raised) is smaller and faster, since it can
basically proceed without considering the exceptional case. The
exception-handling code is out-of-line, to avoid a negative icache
impact.

[Also note that much "handling" of exceptions is not actually "reacting"
to specific exceptions, but simply cleaning up and propagating an error;
in C++, this typically happens via RAII (i.e. "unwind-protect" in lisp),
or "catch(...)", and those have very low space overhead, as they do not
need to do any kind of allocation/copying/matching; mostly they simply
execute the user code to do whatever is needed.]

-Miles
 
I

Ian Collins

Ian Collins said:
On 01/28/12 09:29 AM, Scott Lurndal wrote:


I tried substituting a 'new uint8[xx}' call to replace a malloc with a try
catch block for bad_alloc. I then disassembled the code. One instruction
(to test the return value of the malloc) turned into:


How comes one version has multiple calls to new and the other one call
to malloc?

What was the original source?


uint8 cmd = iocb->get_op_var2();

buf = (uint8 *)malloc(bufsize);
if (buf == NULL) {
iocb->set_rd(IOT_WITH_EXCEPTIONS, RD1_OCS_MPU_PARITY);
return false;
}

switch (cmd) {

vs.

uint8 cmd = iocb->get_op_var2();

try {
buf = new uint8[bufsize];
} catch (std::exception e) {

never catch exceptions by value, always catch by const reference to
avoid slicing.

Good to know, I guess. Since I intend to keep the malloc and forgo
the try/catch clause, the point is however moot.

But it is a typical novice error that leads to false assumptions
regarding exception performance.
However, in this case (modeling a physical device), the best determination
of how to present the failure can be made as close to the cause of the
exception as possible. I would think that for most applications that
can actually recover from an error (as opposed to just catching std::exception
at the top level, printing a message, and exiting), keeping the recovery action
as close to the code that actually failed makes recovery much simpler.

It may well be, but the catch and the throw my still be several calls
away. Using nested small functions is much cleaner with exceptions than
with return codes. In your example you return the state in the object
passed. An exception based design may well pass the state in an
application specific exception.
Yet creating and destroying a temporary std::exception object also counts in
terms of both extra cycles and code footprint. Both of which impact
performance (generally negatively).

Only if the exceptional condition occurs. The normal path of execution
will not be impacted (and will be cleaner and faster than the error
checking case).
And indeed, some simple performance testing of code running under the simulation
(a 15,000 line BPL compile), shows that when I try exceptions in one of the common
allocation paths, the performance of the BPL compile drops from 17,177 records compiled
per minute to 16,585 records compiled per minute - an almost 4% performance degredation;
this is actually worse than I expected for a single conversion from malloc/init to
new/constructor with try/catch - I'll need to dig into this further.

If you are mixing exceptions and return codes, you aren't really making
a fair comparison. It is very likely you could improve the performance
in other ways, such as a specialised allocator. If your application was
designed to use exceptions throughout, I bet you would see a performance
improvement.
40 bytes. more than half a cache line each occurance (and you really should
count the exception object constructor/destructors too). In some cases, it really matters.

No I shouldn't.

It only occurred in your code because you caught by valve. No exception
object is created unless one is thrown. I also quoted more code than I
should (because of the catch by value). Change your code to catch by
const reference and you will see. For example, a bare bones example
with gcc:

bool f( X* iocb ){
try {
uint8_t* buf = new uint8_t[bufsize];
return true;
}
catch (const std::exception& e) {
return false;
}
}

compiles to (removing some labels for brevity):

pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $42, (%esp)
call _Znaj
movl $1, %eax
leave
ret
..L6:
subl $1, %edx
je .L4
movl %eax, (%esp)
call _Unwind_Resume
..L4:
movl %eax, (%esp)
call __cxa_begin_catch
call __cxa_end_catch
xorl %eax, %eax
leave
ret
And placing the catch higher in the call chain is not possible (at least not without
adding a large number of conditionals and carrying a great deal of state in
some derived exception class to the catch).

The state has to be carried somewhere... If it is only required in the
case of an error, the exception is the logical place for it.
 
D

Dombo

Op 27-Jan-12 1:32, Scott Lurndal schreef:
With g++, each specialization of a template results in replication of each
referenced non-inline member function. So for each unique<type>::member_function,
a separate body of code will generated. For only one or two specializations, it
may not be a significant performance impact, but consider a template for a linked
list, which is specialized by several hundred different types in an operating system;
the amount of added code can be considerable.

While not typesafe, a simple double-linked list base class from which all those
types derive, is much more efficient from a icache footprint standpoint and fairly
simple iterators can be built to iterate over the list (using casts).

With judicious use of templates you can have both in C++; type-safe
linked lists without code duplication using the technique explained in
the chapter about template specializations in the book "The C++
programming Language" written by Bjarne Stroustrup. Yes, with this
technique there are a few casts in the template code, but that is a hell
of a lot better than sprinkling casts all over the place.

IMO seeing a lot of casts in C++ code is a bad sign. With a good design
you rarely, if ever, need to cast. That C++ casts stand out more clearly
(and can be easily grepped) than C style cast is a good thing as far as
I am concerned.
 
J

Juha Nieminen

Scott Lurndal said:
The readability of C++ seems to get worse with each new version of the
standard. Once the <XXX> elements (static/dynamic casts, templates) were added
to the language, it became worse than perl for readability.

I believe you are confusing readability with brevity. Not the same
thing.
 
8

88888 Dihedral

在 2012å¹´1月26日星期四UTC+8下åˆ7æ—¶24分11秒,Nick Keighley写é“:
I've seen some pretty nasty C


the what?


what language avoids this? If you do array bound checking it costs. If
you don't then you run a risk. Are there languages that can prove
array bounds won't be breeched?


could you expand on that; I didn't quite follow you. I don't recall
encountering a problem in this area.

I classify methods according to
A. those just modify pass in objects and use temprorary variables on the stack or static allocated buffers on the heap before invoked by messages.
This class is safe for run-time polymorphism.

B. those have to generate any new non-temporary object to be assigned to some objects in return, somewhat like the heap hog eater in this calss.
If any method in this class calls a method in this class, then it becomes a
memrory hog very easily especially in the recursive way.
 

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,769
Messages
2,569,582
Members
45,059
Latest member
cryptoseoagencies

Latest Threads

Top