|
|||
|
on Sat Sep 05 2009, George Neuner <gneuner2-AT-comcast.net> wrote: > On Fri, 4 Sep 2009 17:31:34 CST, David Abrahams <dave@boostpro.com> > wrote: > >> >>on Fri Sep 04 2009, George Neuner <gneuner2-AT-comcast.net> wrote: >> >>> and possibly deadly for real time code. >> >>Well now, that's true. Possibly deadly. Test and measure to find out. > > The whole discussion is meaningless unless you're considering high > performance code ... at minimum soft real time. HPC and soft real time are almost the same as far as this is concerned: you can tolerate something being arbitrarily slow (within reason) as long as you do it extremely rarely. In other words, you can probably afford to use EH, providing you're not throwing in your inner loop. It certainly won't be "deadly." > We are only talking about, at most, milliseconds ... but that might > matter to a real time program on a slower processor - not every > program will be run on 3GHz 64-bit quad-cores. Now you're talking about hard real time, as I understand it. Yes, any operation you might perform could be deadly to such a system if it takes long enough, no matter how rare it is. You have to test and measure /everything/ (not just EH). >>But regardless, catching close to the throw point doesn't help you at >>all if you don't throw. Otherwise, you pay about a 25x penalty per >>stack frame (on my g++ implementation) to unwind. If you can't afford a >>25x slowdown on the EH paths in the rare instance an exception does >>occur, you've got a problem with EH. >> >>> Compiled for maximum speed, on my 3Ghz dual Pentium 4 Windows box, the >>> difference between the timing of the normal return path and the >>> exception return path for a 2 level nested call is 80..200x depending >>> on whether there are local dtor objects (the no objects case is >>> actually worse as there more hidden exception overhead). >> >>Unless you're targeting 64-bit windows, you're not testing a >>particularly good EH implementation. > > ??? 64-bit Windows uses the same SEH implementation as 32-bit. Did I mention SEH? This is a C++ Newsgroup. But, unless I'm mistaken, MS used the Itanium ABI's[1] general C++ EH mechanism for SEH on Win64 as well as in their C++ compiler. > C++ EH is slightly faster than Windows SEH. In VS2008 you can choose > which to use. If you mean the 64-bit compiler has a better C++ EH > implementation, then I have to plead ignorance ... I'll have to look > into it. I am talking about C++ EH. >>> I don't have a Unix or Linux box handy to test GCC natively. I get >>> roughly similar timings using GCC in cygwin. GCC is slightly better >>> with no local objects and VS2008 is slightly better with local >>> objects. >> >>Like MS's Win32, Cygwin also uses a poor EH implementation based on >>setjmp/longjmp. > > VS2008 uses a table based implementation unless you select SEH > handling. That's a recent development, then. In any case, I don't know anything about the quality of that implementation. I can run my tests here on a VM when I get some more time. > Cygwin is an older version of G++ (3.4.5) so that - I don't > have a 4.4 to test so that will definitely make a difference. IIRC it's not a matter of which version of G++ you have; it's a question of whether they've integrated table-based EH into the runtime for the Cygwin platform. Last I checked, nobody had bothered to do that port, so Cygwin still used setjmp/longjmp even with the latest compilers. >>>>The recommended implementation of exceptions certainly does not >>>>provide a cheap throw. On the other hand, if no exceptions happen, >>>>there is no overhead. >>> >>> This isn't true. Even with the table address-range implementation, >>> there is additional work done by the preamble of any function that >>> contains a try block. >> >>I don't think so. What additional work do you think needs to be done in >>that case? And, what's a preamble ;-)? > > The preamble is the code that sets up the call frame. > > Table based EH implementations need no runtime setup to catch > synchronous C++ exceptions, Right. > but if you use OS structured exceptions (in Windows or Linux) I've never heard of such a thing existing on Linux, although I suppose you could view the generalized Itanium ABI EH mechanism that way since it works across languages... > then there is extra EH information placed in the stack frame. ....but that's table-based, so it doesn't need any extra info in the stack frame. >>the compiler has no way of knowing that it doesn't need to keep the >>value of a around for use in the catch block. This effect usually takes >>the form of what's known as "register pressure." As far as I can tell, >>register pressure can only happen with try/catch, though, and not object >>destructors, since destructors have to run whether or not an exception >>is thrown. > > Mostly it translates as memory traffic because register values have to > be flushed to memory ahead of the try block so they can be restored > later if necessary. No, a throw() is just like any other function call; register values get pushed onto the stack in the usual ways and places; a table-based EH implementation knows where to find them. Register pressure means that you don't get to re-use a register for other purposes in the straight-line code because you can't tell that the catch block is unreachable. Footnotes: [1] http://www.codesourcery.com/public/cxx-abi/abi.html, http://download.intel.com/design/Ita...ads/245370.pdf, etc. -- Dave Abrahams BoostPro Computing http://www.boostpro.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
||||
|
||||
|
|
|
|||
|
on Sat Sep 05 2009, Joshua Maurice <joshuamaurice-AT-gmail.com> wrote: > Assuming some level of hardware caching, or virtual memory, it must be > the case. One of the benefit of exceptions is that in a good > implementation, the error handling code is out of line, so the in-line > code, the normal code, is smaller than what it would be with the error > return codes. That's certainly true in principle, but in practice, the last time I checked (several years ago), it wasn't true on any platform I could find. Has something changed? -- Dave Abrahams BoostPro Computing http://www.boostpro.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Sat, 5 Sep 2009 07:55:23 CST, Francis Glassborow
<francis.glassborow@btinternet.com> wrote: >George Neuner wrote: > >> It is true that the normal and exception return paths may have >> different sets of stack objects to clean up; however, in practice, >> significant local data structures tend to survive until the end of the >> function (as opposed to being destroyed in a sub-scope) ... so, in >> reality, the average differences may be slight. > >You are missing the point. In order to keep the normal code simple and >fast many implementations of exceptions have to load exception handling >data if an exception is thrown. Loading that data is both time consuming >and space consuming. However if you do not encounter a throw during the >execution of your program your code will be lean and fast. That's possible ... in a paging OS the exception handling code and data might not be resident until an exception is thrown. But that doesn't explain differences in non-paging implementations. I've worked with diskless embedded systems where the whole program is resident. Exceptions are still much slower than normal returns. Not necessarily to the point of precluding their use entirely, but enough so that you're really careful what you use them for. My embedded C++ work was with various 3.x. versions of G++ and one time I used an ACK based C++ compiler. I haven't worked diskless with G++ v4.x so I can't comment on that. George -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
on Sun Sep 06 2009, Mathias Gaunard <loufoque-AT-gmail.com> wrote: > On 5 sep, 07:01, George Neuner <gneun...@comcast.net> wrote: > >> >Unless you're targeting 64-bit windows, you're not testing a >> >particularly good EH implementation. >> >> ??? 64-bit Windows uses the same SEH implementation as 32-bit. >> >> C++ EH is slightly faster than Windows SEH. In VS2008 you can choose >> which to use. If you mean the 64-bit compiler has a better C++ EH >> implementation, then I have to plead ignorance ... I'll have to look >> into it. > > I found a fairly interesting presentation on exception-handling with > Visual C++ (it's for 2005 though): > www.nwcpp.org/Downloads/2006/ehc.ppt Unless they made VS2008-compiled code link-incompatible with VS2005-compiled code, it would be hard for them to have changed much about how this works. > explains differences between SEH and C++ exception handling, on x86 or > x86-64, with examples and their generated assembly. Wow, that's slide set is fantastic. They really go into the details of the MS implementation. Note: Their C++ EH for x64 seems to have a few (probably negligible) overheads, but it's not at all obvious to me why those overheads should be needed. -- Dave Abrahams BoostPro Computing http://www.boostpro.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Sun, 6 Sep 2009 02:30:49 CST, Mathias Gaunard <loufoque@gmail.com>
wrote: >You may want to try MinGW 4.4 however, it now defaults to no-overhead >exception implementation. >The former ABI, SJLJ, has been deprecated and they're now using >DWARF-2. Is 4.4 available? I though MinGW's 4.x compilers were beta. George -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Sep 6, 1:33 am, David Abrahams <d...@boostpro.com> wrote:
> on Sat Sep 05 2009, Joshua Maurice <joshuamaurice-AT-gmail.com> wrote: > > > Assuming some level of hardware caching, or virtual memory, it must be > > the case. One of the benefit of exceptions is that in a good > > implementation, the error handling code is out of line, so the in-line > > code, the normal code, is smaller than what it would be with the error > > return codes. > > That's certainly true in principle, but in practice, the last time I > checked (several years ago), it wasn't true on any platform I could > find. Has something changed? Sorry, no. At least, I don't know. I was talking in terms of principle as well, though for my simple timing tests, for recent gcc versions on Linux, the overhead is none or quite small, like .1% or smaller, unlike win 32's ~5-10% overhead for a really contrived testcase. I should look at the assembly. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
My take is that exceptions are a cheezy messaging mechanism for sending faults from a module that has them into a module that can hopefully do something about them. Stack unwinding for resource allocation is awfully nice and so it is tough to walk away from that altogether. Still, if your application has some sort of an internal messaging mechanism, then, you may not actually need explicit exceptions. You can't think about to except or not to except without considering your overall fault management strategy. Fault management means you almost need to view each task the user might perform as a transaction of sorts and to me it almost always seems that a service pattern is the way to go. With that in front of you, basic faults that can occur in a computer can be divided into are ones of either resource exhaustion or communications failures and the best way to inject reliability into the system is to lower the amount of times you make a request for resources. Therefor, each user task is almost a transaction that tries to claim what it needs to do the job. memory exhaustion. The enemy of C++ reliability is dynamic allocation and the biggest offender is memory. When I'm actually taking the premium to write in C ++, I avoid doing memory allocation as much as I possibly can, simply to reduce the number of failure points. I wind up with a lot of placement new operators, and there my problem is a big woe of C++'s . Hand in hand with that is making the mental break with a completely dynamic system. Does every name in every field have to be a completely variable length string? I think that's insane. In my latest project I've been working with my own fixed length string class that uses a template parameter to determine its length at compile time. I roll them all into a structure and suddenly my sizeof (mystruct) includes everything I need. Sure, I have a much larger object than I would have compared to a dynamically allocated everything chumpy, but, for even a trivial name and address object consisting of first, last, street, city, state, zip and email, you have 8 points of failure you really should worry about - the new plus the new on each of the members. Or, I have one structure that if I new, that's that. It's a - once I have my object it will always work pattern, and that's pretty nice. other Other exhaustion problems cannot be made to disappear so easily. For example, a database hit or reading a socket or writing to a file is going to fail. There, an exception may not actually be the right thing to do. In the case of the database, you might be able to get away with it if your objects are perfect and your transaction lifetime is tied to your C++ stack. But for that I have an error checking class that says if I did something stupid during development, just fault on the breakpoint immediately, or, maybe return a failure or throw an exception (depending on my mood). The perfect thing, which I rarely have done, is to have a realistic retry option on the action - but set so the user can do something. Like, you could have an error checking function mechanism that would be better than an exception handler if it perhaps popped up a dialog and told the user he or she needed to be connected to the internet, delete some other files, or so forth. For non-resource problems, I fault the program immediately. The thing is, if there is a bug, don't try and have your program fix it by throwing an exception and handling it somehow, have yourself fix it. It's not an exception if you go past the end of an array, or try to, its an error, and you should have a unit test suite for all of your objects so that problems like this can be smoketested out and you can get a good regression test when you run. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
> er, actually using exception requires you rethink some common senses, > for example, when I found such code in C++ source, I don't know how to > refractor it indeed: > Code:
> foo* f = new foo;
> if (!f)
> {
> // log error here
> return false}
>
>
> the checking here is definitely useless. I have two options here: No, its not useless. In some C++ implementations, the default is to not have exceptions get thrown when new fails, and I think they might even allow the symantec of having new not throw exceptions but still let you use exceptions elsewhere. Do be careful, Mr. Bond. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
TJ Bandrowsky wrote:
> My take is that exceptions are a cheezy messaging mechanism for > (...) > > memory exhaustion. > The enemy of C++ reliability is dynamic allocation and the biggest > offender is memory. When I'm actually taking the premium to write in C It is? What kind of system are you working on that you actually have to worry about new failing? > ++, I avoid doing memory allocation as much as I possibly can, simply > to reduce the number of failure points. I wind up with a lot of > placement new operators, and there my problem is a big woe of C++'s . If we told our developers to start using placement new I'm pretty sure we'd get more bugs from that than from failing new, because in the 4 years in this job I have seen failing new[] exactly one (1) time and that was when allocating a 300MB buffer. Granted, I'm on Windows only, so for low-memory environments YMMV. > Hand in hand with that is making the mental break with a completely > dynamic system. Does every name in every field have to be a > completely variable length string? I think that's insane. In my Hmmm ... It doesn't have to be, but under normal circumstances it's much easier for everyone if it just is the way it is - and variable length strings do appear to be the default for most higher-level languages. > latest project I've been working with my own fixed length string class > that uses a template parameter to determine its length at compile > time. I roll them all into a structure and suddenly my sizeof > (mystruct) includes everything I need. Sure, I have a much larger > object than I would have compared to a dynamically allocated > everything chumpy, but, for even a trivial name and address object > consisting of first, last, street, city, state, zip and email, you > have 8 points of failure you really should worry about - the new plus > the new on each of the members. Or, I have one structure that if I > new, that's that. It's a - once I have my object it will always work > pattern, and that's pretty nice. > I guess there are a few valid reasons to have such a system in place, but failing new because out-of-memory for a few-bytes allocations for strings doesn't seem to strike me as one of them. You reduce the theoretical points of failure by increasing code complexity and by introducing new point-of-failure when your strings are too short. So maybe I should ask again on what system you are working with C++ that you think the whole hassle is worth it ? cheers, Martin -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
TJ Bandrowsky wrote:
>> er, actually using exception requires you rethink some common senses, >> for example, when I found such code in C++ source, I don't know how to >> refractor it indeed: >> Code:
>> foo* f = new foo;
>> if (!f)
>> {
>> // log error here
>> return false}
>>
>>
>> the checking here is definitely useless. I have two options here: > > No, its not useless. In some C++ implementations, the default is to > not have exceptions get thrown when new fails, and I think they might > even allow the symantec of having new not throw exceptions but still > let you use exceptions elsewhere. Do be careful, Mr. Bond. If new returns a null pointer when it fails then you are not using C++ just a language that looks like it :-) If the programmer wants the no throwing version he must specify it by using new(nothrow) -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On 8 sep, 16:42, TJ Bandrowsky <tbandrow...@treatyist.com> wrote:
> > er, actually using exception requires you rethink some common senses, > > for example, when I found such code in C++ source, I don't know how to > > refractor it indeed: > > Code:
> > foo* f = new foo;
> > if (!f)
> > {
> > // log error here
> > return false}
Code:
> > > > > > the checking here is definitely useless. I have two options here: > > No, its not useless. In some C++ implementations, the default is to > not have exceptions get thrown when new fails And that is not standard conforming. This group is about standard C++, not about a C++-inspired sublanguage. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Tue, 8 Sep 2009 13:16:37 CST, "Martin T." <0xCDCDCDCD@gmx.at>
wrote: >TJ Bandrowsky wrote: >> My take is that exceptions are a cheezy messaging mechanism for >> (...) >> >> memory exhaustion. >> The enemy of C++ reliability is dynamic allocation and the biggest >> offender is memory. When I'm actually taking the premium to write in C > >It is? What kind of system are you working on that you actually have to >worry about new failing? Virtually any diskless embedded system. C++ still doesn't (and may never) have the traction C does in this market, but there are a lot of people using it for embedded work. >> Hand in hand with that is making the mental break with a completely >> dynamic system. Does every name in every field have to be a >> completely variable length string? I think that's insane. In my > >Hmmm ... It doesn't have to be, but under normal circumstances it's much >easier for everyone if it just is the way it is - and variable length >strings do appear to be the default for most higher-level languages. Strings in general are a problem in tight memory situations. If the application need strings at all, I generally prefer to intern them so there is never more than one runtime copy of any particular string. George -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
Mathias Gaunard wrote:
> On 8 sep, 16:42, TJ Bandrowsky <tbandrow...@treatyist.com> wrote: >>> er, actually using exception requires you rethink some common senses, >>> for example, when I found such code in C++ source, I don't know how to >>> refractor it indeed: >>> Code:
>>> foo* f = new foo;
>>> if (!f)
>>> {
>>> // log error here
>>> return false}
>>>
>> No, its not useless. In some C++ implementations, the default is to >> not have exceptions get thrown when new fails > > And that is not standard conforming. > This group is about standard C++, not about a C++-inspired sublanguage. > I think I missed the part where this group had std in it's name. I find the note quite valid, although I disagree with the conclusion the it's not useless in general. It's only use is if you *know* you are on a compiler with that behaviour. It's not as if Visual Studio 6 has suddenly vanished from the face of the earth. I fear there are still quite a few people using it, VC6 *will* return a nullptr. cheers, Martin -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
George Neuner wrote:
> On Fri, 4 Sep 2009 07:35:47 CST, George Neuner <gneuner2@comcast.net> > wrote: > >>My test code is attached to this post. > > Sorry everybody ... the moderators rejected the file attachment. > > Here is the test code. As mentioned previously, it is Windows centric > using CPU performance counters ... you'll have to modify the timing > for your own platform. Also, 32-bit VS uses a separate abs64() > function for 64-bit ints. > > Times are in seconds on a 3GHz dual Pentium 4. I timed the normal > return path and the exception return path (average of 1000 tests) for > call depths from 1 to 10 nesting levels. Have you taken a close look at the measurement results? Because the results for the normal return path are suspicious. Larger call-stacks take less time? It seems your test program has a bug. <snip> > ///////////////////////////////////////////// > > void func( int depth, bool excpt, __int64 *time ) > { > #if LOCAL_OBJECTS > Garbage g; > #endif > > if ( depth > 0 ) > func( depth-1, excpt, time ); > > QueryPerformanceCounter( (LARGE_INTEGER*) time ); And the bug is right here. You only measure the time for returning from the *first* call to func. > if ( excpt ) > throw -1; > } > <snip> Bart v Ingen Schenau -- a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq c.l.c FAQ: http://c-faq.com/ c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/ [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|||
|
On Fri, 11 Sep 2009 14:27:51 CST, Bart van Ingen Schenau
<bart@ingen.ddns.info> wrote: >George Neuner wrote: > >> On Fri, 4 Sep 2009 07:35:47 CST, George Neuner <gneuner2@comcast.net> >> wrote: >> >>>My test code is attached to this post. >> >> Sorry everybody ... the moderators rejected the file attachment. >> >> Here is the test code. As mentioned previously, it is Windows centric >> using CPU performance counters ... you'll have to modify the timing >> for your own platform. Also, 32-bit VS uses a separate abs64() >> function for 64-bit ints. >> >> Times are in seconds on a 3GHz dual Pentium 4. I timed the normal >> return path and the exception return path (average of 1000 tests) for >> call depths from 1 to 10 nesting levels. > >Have you taken a close look at the measurement results? Because the >results for the normal return path are suspicious. Larger call-stacks >take less time? >It seems your test program has a bug. > ><snip> >> ///////////////////////////////////////////// >> >> void func( int depth, bool excpt, __int64 *time ) >> { >> #if LOCAL_OBJECTS >> Garbage g; >> #endif >> >> if ( depth > 0 ) >> func( depth-1, excpt, time ); >> >> QueryPerformanceCounter( (LARGE_INTEGER*) time ); > >And the bug is right here. You only measure the time for returning from >the *first* call to func. > >> if ( excpt ) >> throw -1; >> } >> ><snip> > >Bart v Ingen Schenau You're right about the bug. With it fixed there is still an average 15x difference between the normal return path and the exception return path. Regarding variations in timing - they are to be expected. Even at maximum priority, device drivers can interfere with process execution. On a modern OS, it's almost impossible to achieve a completely quiescent system to test with. The first exception throw seems always to be the costliest, lending credence to Francis's speculation that the handlers are only paged in when the exception is thrown. I tried adding a non-timed exception throwing call ahead of the exception timing loop to make sure the handler code is resident, but it doesn't seem to make any difference to the average timing. George -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|