Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Why does std::stack::pop() not throw an exception if the stack is empty?

Reply
Thread Tools

Why does std::stack::pop() not throw an exception if the stack is empty?

 
 
Ian Collins
Guest
Posts: n/a
 
      02-05-2011
On 02/ 5/11 02:18 PM, Joshua Maurice wrote:
>
> Here's a linux test for comparison:
>
> psflor.informatica.com ~$ uname -a
> Linux<host-name> 2.6.18-128.el5 #1 SMP Wed Dec 17 11:41:38 EST 2008
> x86_64 x86_64 x86_64 GNU/Linux
>
> Test results for one execution (with output rearranged):
>
> Manually Inlined Optimized Return Code : 5.21
> Manually Inlined Return Code : 5.21
> Inlineable Global Return Code : 5.21
> Inlineable Member Return Code : 5.21
> Virtual Return Code : 10.42
> Virtual Exception : 9.33
> Virtual Return Code Fake Try : 10.41


Sun CC on Solaris gives similar relative results:

Manually Inlined Optimized Return Code : 2.62
Manually Inlined Return Code : 2.61
Inlineable Global Return Code : 2.6
Inlineable Member Return Code : 2.65
Virtual Return Code : 11.79
Virtual Exception : 10.48
Virtual Return Code Fake Try : 13.02

--
Ian Collins
 
Reply With Quote
 
 
 
 
Andre Kaufmann
Guest
Posts: n/a
 
      02-05-2011
On 05.02.2011 02:18, Joshua Maurice wrote:
> On Feb 4, 9:49 am, Andre Kaufmann<(E-Mail Removed)> wrote:
>> On 04.02.2011 07:49, Joshua Maurice wrote:

> ...
>
> Ok. Here we go. My test code is at the end. All results are from runs
> executed today.


Thank you for the test code and your work. That's quite fair and a good
basis for discussions.

And then it's fair enough to invest some time by my own.

> [...]
> Also, if you see any problems with my test, please say so, so I can
> fix it and rerun it.


No problem, but some remarks:

- Windows timers based on TickCount and Clock are quite inaccurate,
since they are updated on each IRQ and therefore have a resolution of
15ms (on most systems). For long running tests (such as this) and
to get a first impression they are o.k.
I prefer PerformanceCounters for high precision timings
(but for this test I don't think it makes a difference)

- From the command line parameters I conclude you are using VS 2008.
I use VS 2010. I don't think that they've changed the exception model
completely in VS2010 - but who knows I've gonna check this at work
next week. There is a free version of VS2010 available, but I don't
know if the code optimization is restricted (AFAIK no - since 2010).

I run the tests on my laptop - Intel dual core 2.2 GHz.
I used the same parameters, but full optimization -> but shouldn't have
a significant effect.

C++ command line:
/Zi /nologo /W3 /WX- /Ox /Oi /Ot /GL /D "WIN32" /D "NDEBUG" /D
"_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm- /EHsc /GS /Gy /fprecise
/Zc:wchar_t /Zc:forScope /Yu"StdAfx.h" /Fp"x64\Release\ForumCpp.pch"
/Fa"x64\Release\" /Fo"x64\Release\" /Fd"x64\Release\vc100.pdb" /Gd
/errorReport:queue

Linker:

/OUT:"Cpp.exe" /INCREMENTAL:NO /NOLOGO "kernel32.lib" "user32.lib"
"gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib"
"ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib"
/MANIFEST /ManifestFile:"x64\Release\Cpp.exe.intermediate.man ifest"
/ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG
/PDB:"trash\Cpp.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /PGD:"Cpp.pgd"
/LTCG /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X64 /ERRORREPORT:QUEUE

I've got the following results: (rearranged output)

Command line: 60000 -10 1:

Inlineable Member Return Code : 3.182
Inlineable Global Return Code : 3.151
Manually Inlined Return Code : 3.198
Virtual Return Code : 14.368
Virtual Return Code Fake Try : 17.394
Manually Inlined Optimized Return Code : 3.182
Virtual Exception : 14.243


Command line: 60000 -10:

Inlineable Member Return Code : 3.26
Inlineable Global Return Code : 3.198
Manually Inlined Return Code : 3.198
Virtual Return Code : 14.259
Virtual Return Code Fake Try : 17.459
Manually Inlined Optimized Return Code : 3.183
Virtual Exception : 17.487


Nearly the same results, besides some neglectable differences.
Only one result shows a difference:
After I've changed the code

class TestImpl2 : public TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum)
{
// cout << "XXX" << endl;
}
virtual ReturnCodeT virtualReturnCode(int a, int b, ...
{ //cout << "XXX" << endl;
return Success;
}
};

into

class TestImpl2 : public TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum)
{
if (a + b == targetSum)
cout << "XXX" << endl;
}
virtual ReturnCodeT virtualReturnCode(int a, int b, int ... { //cout
<< "XXX" << endl;
if (a + b == targetSum)
cout << "XXX" << endl;
return Success;
}
};

I've got the same results.
So far I don't experience speed differences.

> However, sadly too many compiler writers and ABI writers missed this
> very important memo, which I think is core to the entire C++


For x86 Windows I agree.
But let's have a look at the assembly code of your other test code:

int main(int argc, char* argv[])
{
try
{
if (argc == 3) throw 1;
}
catch(int)
{
return -1;
}
return 0;
}

Windows x86 code VC2010:

00BA1002 in al,dx
00BA1003 push 0FFFFFFFFh
00BA1005 push offset __ehhandler$_wmain (0BA1980h)
00BA100A mov eax,dword ptr fs:[00000000h]
00BA1010 push eax
00BA1011 mov dword ptr fs:[0],esp
00BA1018 sub esp,8
try
{
if (argc == 3) throw 1;
00BA101B cmp dword ptr [ebp+8],3
00BA101F push ebx
00BA1020 push esi
00BA1021 push edi
00BA1022 mov dword ptr [ebp-10h],esp
00BA1025 mov dword ptr [ebp-4],0
00BA102C jne $LN8+14h (0BA105Dh)
00BA102E push offset __TI1H (0BA22C8h)
00BA1033 lea eax,[ebp-14h]
00BA1036 push eax
00BA1037 mov dword ptr [ebp-14h],1
00BA103E call _CxxThrowException (0BA1970h)
}
catch(int)
{
return -1;
00BA1043 mov eax,offset $LN8 (0BA1049h)
00BA1048 ret
$LN8:
00BA1049 or eax,0FFFFFFFFh
}
return 0;
}
00BA104C mov ecx,dword ptr [ebp-0Ch]
00BA104F mov dword ptr fs:[0],ecx
00BA1056 pop edi
00BA1057 pop esi
00BA1058 pop ebx
00BA1059 mov esp,ebp
00BA105B pop ebp
00BA105C ret
00BA105D mov ecx,dword ptr [ebp-0Ch]
00BA1060 pop edi
00BA1061 pop esi
00BA1062 xor eax,eax
00BA1064 mov dword ptr fs:[0],ecx
00BA106B pop ebx
00BA106C mov esp,ebp
00BA106E pop ebp
00BA106F ret


Windows x64 code VC2010:

{
if (argc == 3) throw 1;
000000013F13100D cmp ecx,3
000000013F131010 jne wmain+2Ch (13F13102Ch)
000000013F131012 mov dword ptr [rsp+20h],1


000000013F13101A lea rdx,[_TI1H (13F1324A0h)]
000000013F131021 lea rcx,[rsp+20h]
000000013F131026 call _CxxThrowException (13F13190Eh)
000000013F13102B nop
}
return 0;


000000013F13102C xor eax,eax
000000013F13102E jmp $LN8+3 (13F131033h)
{
return -1;
000000013F131030 or eax,0FFFFFFFFh
}
000000013F131033 add rsp,38h
000000013F131037 ret


There is a huge difference. In x86 code the old implementation is used
-> exception stack which pointer is held in [fs] segment register.
In x64 code there is no exception stack anymore since the compiler uses
a static table for stack unwinding, therefore no overhead if no
exception is thrown.
So the implementation should be comparable to Linux / Unix systems and
compilers.

Since I already mentioned that SEH doesn't add any overhead (if the
compiler ignores SEH exceptions and doesn't need to track them if
thrown) it's the compilers fault if there is a speed difference.

And I don't think for example GCC under Windows uses a different
exception model than under Linux ? Or does it ?


>[...]

 
Reply With Quote
 
 
 
 
Juha Nieminen
Guest
Posts: n/a
 
      02-05-2011
Miles Bader <(E-Mail Removed)> wrote:
> But remember that in typical C++ code you'll usually have many more
> _implicit_ try-catch blocks, inserted by the compiler to call
> the destructors of local objects.
>
> So you can't judge the cost simply by looking for "try" in your source code...


You don't need to judge the cost of throwing (and catching) at all,
because they are *expected* to be somewhat heavy operations (relatively
speaking, at least). What really matters is that if nothing throws, your
code will basically be as fast as if the compiler didn't support exceptions
at all (in other words, no machine opcodes at all will be added by the
compiler to the normal execution path in order to support exceptions).

(The only possible overhead you can get from exceptions in the normal
execution path is caused by the additional conditionals you yourself
explicitly write in order to check the things that could throw.)
 
Reply With Quote
 
Tiib
Guest
Posts: n/a
 
      02-05-2011
On Feb 4, 5:03*pm, Michael Doubez <(E-Mail Removed)> wrote:
> On 4 fv, 14:03, Goran <(E-Mail Removed)> wrote:
>
> > On Feb 4, 11:21*am, "Paul" <(E-Mail Removed)> wrote:

>
> > > > My 2c: pop is a cleanup operation, and as such, it should have a
> > > > nothrow exception safety guarantee.

>
> > > It calls the destructor of contained object; a nothrow guarantee would
> > > mean that you silently discard exceptions thrown from the object's
> > > destructor. Not what you generally want.

>
> > I disagree very much. Throwing destructor does not matter. Throwing
> > destructor is a massive bug, reasons aplenty. If it happens, I'd quite
> > frankly like to terminate more than anything else.

>
> This is an open subject. The only thing forbidden is throwing from the
> destructor of during an exception stack unwinding and there are ways
> to avoid that.


What ways you mean? Is there something like:

bool std::unwinding(); // true during stack unwinding


> There is at least one library where throwing from the destructor is
> legitimate (an sql library IIRC); it uses RAI for commit/execute of
> the SQL query. An exception is thrown upon error.
>
> However, I prefer a std::logic_error thrown from a destructor and
> being warned (even if it kills the program) than having something
> catch it silently and let the program continue to run in a incorrect
> state.


Goran is right. *generally* throwing from cleanup functions or
destructors is unexpected feature. I told it to die ... wtf it throws
now that it has two left legs and so can't die? It should throw when
second left leg was added to it.

Unexpected behavior is always confusing and annoying even when there
are workarounds. std::stack is generic standard container and on
several of cases throwing from pop() would be unexpected complication
for its users as would be throwing from destructors of elements of
stack.

I would just avoid using a SQL library with special design that throws
from destructors; there are plenty of SQL libraries that do not. Using
destructors on exit from scope to do silently lot of other things but
cleaning up is abuse of destructors and confusing at best.
 
Reply With Quote
 
Bo Persson
Guest
Posts: n/a
 
      02-05-2011
Juha Nieminen wrote:
> Miles Bader <(E-Mail Removed)> wrote:
>> But remember that in typical C++ code you'll usually have many more
>> _implicit_ try-catch blocks, inserted by the compiler to call
>> the destructors of local objects.
>>
>> So you can't judge the cost simply by looking for "try" in your
>> source code...

>
> You don't need to judge the cost of throwing (and catching) at all,
> because they are *expected* to be somewhat heavy operations
> (relatively speaking, at least). What really matters is that if
> nothing throws, your code will basically be as fast as if the
> compiler didn't support exceptions at all (in other words, no
> machine opcodes at all will be added by the compiler to the normal
> execution path in order to support exceptions).
>
> (The only possible overhead you can get from exceptions in the
> normal execution path is caused by the additional conditionals you
> yourself explicitly write in order to check the things that could
> throw.)


Arguably, this isn't overhead either, as you reasonably would have to
check these conditions anyway.


Bo Persson


 
Reply With Quote
 
Miles Bader
Guest
Posts: n/a
 
      02-05-2011
Juha Nieminen <(E-Mail Removed)> writes:
>> But remember that in typical C++ code you'll usually have many more
>> _implicit_ try-catch blocks, inserted by the compiler to call
>> the destructors of local objects.
>>
>> So you can't judge the cost simply by looking for "try" in your source code...

>
> You don't need to judge the cost of throwing (and catching) at all,
> because they are *expected* to be somewhat heavy operations (relatively
> speaking, at least). What really matters is that if nothing throws, your
> code will basically be as fast as if the compiler didn't support exceptions
> at all (in other words, no machine opcodes at all will be added by the
> compiler to the normal execution path in order to support exceptions).


Yes, I know that's true for some ABI's (modern gcc on linux, etc), but
in this case, the subject was VC++'s 32-bit ABI, where try-catch blocks
do incur a runtime cost, even when no except is thrown.

-Miles

--
Youth, n. The Period of Possibility, when Archimedes finds a fulcrum,
Cassandra has a following and seven cities compete for the honor of endowing a
living Homer.
 
Reply With Quote
 
Tiib
Guest
Posts: n/a
 
      02-05-2011
On Feb 5, 3:41*am, Miles Bader <(E-Mail Removed)> wrote:
> Leigh Johnston <(E-Mail Removed)> writes:
> >> But remember that in typical C++ code you'll usually have many more
> >> _implicit_ try-catch blocks, inserted by the compiler to call
> >> the destructors of local objects.

>
> >> So you can't judge the cost simply by looking for "try" in your
> >> source code...

>
> > I wasn't. *IIRC there was no overhead in a function which contained a
> > throw statement if the throw was not invoked even taking RAII into
> > account.

>
> No, my point is that in a VC++ 32-bit style ABI there's overhead in the
> no-exception-thrown path for every use of RAII if an exception can
> _possibly_ occur, compared to a more modern ABI.


Is it really so? I think that stack is untouched when exception flows
through. If nothing catches then no destructors run despite your raii-
shmaii and application exits.

> Since use of RAII is of course very common in typical C++ code, this is
> can be an issue.
>
> > The path that needs to be fast is the most common path; the
> > most common path is the path in which an exception is not thrown

>
> Of course; but this path _is_ often affected, because of RAII (despite
> the lack of explicit "try" statements).


Sounds strange concept. What MS compiler does it?
 
Reply With Quote
 
Michael Doubez
Guest
Posts: n/a
 
      02-05-2011
On 5 fv, 11:01, Tiib <(E-Mail Removed)> wrote:
> On Feb 4, 5:03*pm, Michael Doubez <(E-Mail Removed)> wrote:
>
>
>
> > On 4 fv, 14:03, Goran <(E-Mail Removed)> wrote:

>
> > > On Feb 4, 11:21*am, "Paul" <(E-Mail Removed)> wrote:

>
> > > > > My 2c: pop is a cleanup operation, and as such, it should have a
> > > > > nothrow exception safety guarantee.

>
> > > > It calls the destructor of contained object; a nothrow guarantee would
> > > > mean that you silently discard exceptions thrown from the object's
> > > > destructor. Not what you generally want.

>
> > > I disagree very much. Throwing destructor does not matter. Throwing
> > > destructor is a massive bug, reasons aplenty. If it happens, I'd quite
> > > frankly like to terminate more than anything else.

>
> > This is an open subject. The only thing forbidden is throwing from the
> > destructor of during an exception stack unwinding and there are ways
> > to avoid that.

>
> What ways you mean? Is there something like:
>
> *bool std::unwinding(); // true during stack unwinding


There is std::uncaught_exception(). If it returns true, then your are
in a stack unwinding due to exception and you can refrain from
throwing.

>
> > There is at least one library where throwing from the destructor is
> > legitimate (an sql library IIRC); it uses RAI for commit/execute of
> > the SQL query. An exception is thrown upon error.

>
> > However, I prefer a std::logic_error thrown from a destructor and
> > being warned (even if it kills the program) than having something
> > catch it silently and let the program continue to run in a incorrect
> > state.

>
> Goran is right. *generally* throwing from cleanup functions or
> destructors is unexpected feature. I told it to die ... wtf it throws
> now that it has two left legs and so can't die? It should throw when
> second left leg was added to it.


Are you sure that all your destructor only calls functions that haver
the nothrow guarantee ? I am not so sure about mine.

> Unexpected behavior is always confusing and annoying even when there
> are workarounds. std::stack is generic standard container


nitpicking: it is a standard container adapter.

By the way, if I change the underlying container, it could well be a
container that throws upon pop_back/pop_front (by example specialy
designed to allow it).

Well, that's purely theoretical and I agree that not throwing from
destructor is the rule unless you have very good reason to break it.

> and on
> several of cases throwing from pop() would be unexpected complication
> for its users as would be throwing from destructors of elements of
> stack.
>
> I would just avoid using a SQL library with special design that throws
> from destructors; there are plenty of SQL libraries that do not. Using
> destructors on exit from scope to do silently lot of other things but
> cleaning up is abuse of destructors and confusing at best.


I have not used it personnaly but I remember looking at the
documentation and it made sense and it what quite a nice expressive
syntax.

--
Michael
 
Reply With Quote
 
Michael Doubez
Guest
Posts: n/a
 
      02-05-2011
On 4 fv, 18:43, James Kanze <(E-Mail Removed)> wrote:
> On Feb 4, 3:06 pm, Michael Doubez <(E-Mail Removed)> wrote:
>
> > On 4 fv, 14:01, Juha Nieminen <(E-Mail Removed)> wrote:
> > > Michael Doubez <(E-Mail Removed)> wrote:
> > > > I don't think so. pop() can throw an exception from the underlying
> > > > container or the contained element's destructor.
> > > * Isn't it a kind of hard rule that destructors must never throw?

> > There has been a proposal IIRC but there are some case where it is a
> > valid design.

>
> There are certain, very special cases where it is appropriate
> for a destructor to throw. *But only in such very special cases.
> Such classes can't be put into a standard container, since
> (17.4.3.6):
>
> * * In particular, the effects are undefined in the
> * * following cases:
> * * * * [...]
> * * * * if any replacement function or handler function or
> * * * * destructor operation throws an exception, unless
> * * * * specifically allowed in the applicable Required
> * * * * behavior paragraph.
>
> A type used in a standard container is not allowed to exit
> a destructor via an exception.


Thanks, I didn't know that. Well, that cuts my argument.

Does this apply to standard adaptors ?

--
Michael
 
Reply With Quote
 
Joshua Maurice
Guest
Posts: n/a
 
      02-05-2011
On Feb 5, 12:58*am, Andre Kaufmann <(E-Mail Removed)> wrote:
> On 05.02.2011 02:18, Joshua Maurice wrote:
>
> > On Feb 4, 9:49 am, Andre Kaufmann<(E-Mail Removed)> *wrote:
> >> On 04.02.2011 07:49, Joshua Maurice wrote:

> > ...

>
> > Ok. Here we go. My test code is at the end. All results are from runs
> > executed today.

>
> Thank you for the test code and your work. That's quite fair and a good
> basis for discussions.
>
> And then it's fair enough to invest some time by my own.
>
> > [...]
> > Also, if you see any problems with my test, please say so, so I can
> > fix it and rerun it.

>
> No problem, but some remarks:
>
> - Windows timers based on TickCount and Clock are quite inaccurate,
> * *since they are updated on each IRQ and therefore have a resolution of
> * *15ms (on most systems). For long running tests (such as this) and
> * *to get a first impression they are o.k.
> * *I prefer PerformanceCounters for high precision timings
> * *(but for this test I don't think it makes a difference)
>
> - From the command line parameters I conclude you are using VS 2008.
> * *I use VS 2010. I don't think that they've changed the exception model
> * *completely in VS2010 - but who knows I've gonna check this at work
> * *next week. There is a free version of VS2010 available, but I don't
> * *know if the code optimization is restricted (AFAIK no - since 2010).
>
> I run the tests on my laptop - Intel dual core 2.2 GHz.
> I used the same parameters, but full optimization -> but shouldn't have
> a significant effect.
>
> C++ command line:
> /Zi /nologo /W3 /WX- /Ox /Oi /Ot /GL /D "WIN32" /D "NDEBUG" /D
> "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm- /EHsc /GS /Gy /fprecise
> /Zc:wchar_t /Zc:forScope /Yu"StdAfx.h" /Fp"x64\Release\ForumCpp.pch"
> /Fa"x64\Release\" /Fo"x64\Release\" /Fd"x64\Release\vc100.pdb" /Gd
> /errorReport:queue
>
> Linker:
>
> /OUT:"Cpp.exe" /INCREMENTAL:NO /NOLOGO "kernel32.lib" "user32.lib"
> "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib"
> "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib"
> /MANIFEST /ManifestFile:"x64\Release\Cpp.exe.intermediate.man ifest"
> /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG
> /PDB:"trash\Cpp.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /PGD:"Cpp.pgd"
> /LTCG /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X64 /ERRORREPORT:QUEUE
>
> I've got the following results: (rearranged output)
>
> Command line: 60000 -10 1:
>
> * *Inlineable Member * *Return Code * * * : 3.182
> * *Inlineable Global * *Return Code * * * : 3.151
> * *Manually Inlined * * * * * Return Code : 3.198
> * *Virtual Return Code * * * * * * * * * *: 14.368
> * *Virtual Return Code *Fake Try * * * * *: 17.394
> * *Manually Inlined Optimized Return Code : 3.182
> * *Virtual Exception * * * * * * * * * * *: 14.243
>
> Command line: 60000 -10:
>
> * *Inlineable Member * *Return Code * * * : 3.26
> * *Inlineable Global * *Return Code * * * : 3.198
> * *Manually Inlined * * * * * Return Code : 3.198
> * *Virtual Return Code * * * * * * * * * *: 14.259
> * *Virtual Return Code *Fake Try * * * * *: 17.459
> * *Manually Inlined Optimized Return Code : 3.183
> * *Virtual Exception * * * * * * * * * * *: 17.487
>
> Nearly the same results, besides some neglectable differences.
> Only one result shows a difference:
> After I've changed the code
>
> class TestImpl2 : public TestInterface
> {
> public:
> * * * * virtual void virtualCanThrow(int a, int b, int targetSum)
> * * * * {
> * * * * * * * * // cout << "XXX" << endl;
> * * * * }
> * * * * virtual ReturnCodeT virtualReturnCode(int a, int b, ...
> * * * * { * //cout << "XXX" << endl;
> * * * * return Success;
> * * * * }
>
> };
>
> into
>
> class TestImpl2 : public TestInterface
> {
> public:
> * * * * virtual void virtualCanThrow(int a, int b, int targetSum)
> * * * * {
> * * * * * * * * *if (a + b == targetSum)
> * * * * * * * * * * * * *cout << "XXX" << endl;
> * * * * }
> * * * * virtual ReturnCodeT virtualReturnCode(int a, int b, int .... * * { * //cout
> << "XXX" << endl;
> * * * * * * * * *if (a + b == targetSum)
> * * * * * * * * * * * * *cout << "XXX" << endl;
> * * * * return Success;
> * * * * }
>
> };
>
> I've got the same results.
> So far I don't experience speed differences.
>
> *> However, sadly too many compiler writers and ABI writers missed this
> *> very important memo, which I think is core to the entire C++
>
> For x86 Windows I agree.
> But let's have a look at the assembly code of your other test code:
>
> int main(int argc, char* argv[])
> {
> * * * * try
> * * * * {
> * * * * * * * * if (argc == 3) throw 1;
> * * * * }
> * * * * catch(int)
> * * * * {
> * * * * * * * * return -1;
> * * * * }
> * * * * return 0;
>
> }
>
> Windows x86 code VC2010:
>
> 00BA1002 *in * * * * *al,dx
> 00BA1003 *push * * * *0FFFFFFFFh
> 00BA1005 *push * * * *offset __ehhandler$_wmain (0BA1980h)
> 00BA100A *mov * * * * eax,dword ptr fs:[00000000h]
> 00BA1010 *push * * * *eax
> 00BA1011 *mov * * * * dword ptr fs:[0],esp
> 00BA1018 *sub * * * * esp,8
> * * * * try
> * * * * {
> * * * * * * * * if (argc == 3) throw 1;
> 00BA101B *cmp * * * * dword ptr [ebp+8],3
> 00BA101F *push * * * *ebx
> 00BA1020 *push * * * *esi
> 00BA1021 *push * * * *edi
> 00BA1022 *mov * * * * dword ptr [ebp-10h],esp
> 00BA1025 *mov * * * * dword ptr [ebp-4],0
> 00BA102C *jne * * * * $LN8+14h (0BA105Dh)
> 00BA102E *push * * * *offset __TI1H (0BA22C8h)
> 00BA1033 *lea * * * * eax,[ebp-14h]
> 00BA1036 *push * * * *eax
> 00BA1037 *mov * * * * dword ptr [ebp-14h],1
> 00BA103E *call * * * *_CxxThrowException (0BA1970h)
> * * * * }
> * * * * catch(int)
> * * * * {
> * * * * * * * * return -1;
> 00BA1043 *mov * * * * eax,offset $LN8 (0BA1049h)
> 00BA1048 *ret
> $LN8:
> 00BA1049 *or * * * * *eax,0FFFFFFFFh
> * * * * }
> * * * * return 0;}
>
> 00BA104C *mov * * * * ecx,dword ptr [ebp-0Ch]
> 00BA104F *mov * * * * dword ptr fs:[0],ecx
> 00BA1056 *pop * * * * edi
> 00BA1057 *pop * * * * esi
> 00BA1058 *pop * * * * ebx
> 00BA1059 *mov * * * * esp,ebp
> 00BA105B *pop * * * * ebp
> 00BA105C *ret
> 00BA105D *mov * * * * ecx,dword ptr [ebp-0Ch]
> 00BA1060 *pop * * * * edi
> 00BA1061 *pop * * * * esi
> 00BA1062 *xor * * * * eax,eax
> 00BA1064 *mov * * * * dword ptr fs:[0],ecx
> 00BA106B *pop * * * * ebx
> 00BA106C *mov * * * * esp,ebp
> 00BA106E *pop * * * * ebp
> 00BA106F *ret
>
> Windows x64 code VC2010:
>
> * * * * {
> * * * * * * * * if (argc == 3) throw 1;
> 000000013F13100D *cmp * * * * ecx,3
> 000000013F131010 *jne * * * * wmain+2Ch (13F13102Ch)
> 000000013F131012 *mov * * * * dword ptr [rsp+20h],1
>
> 000000013F13101A *lea * * * * rdx,[_TI1H (13F1324A0h)]
> 000000013F131021 *lea * * * * rcx,[rsp+20h]
> 000000013F131026 *call * * * *_CxxThrowException (13F13190Eh)
> 000000013F13102B *nop
> * * * * }
> * * * * return 0;
>
> 000000013F13102C *xor * * * * eax,eax
> 000000013F13102E *jmp * * * * $LN8+3 (13F131033h)
> * * * * {
> * * * * return -1;
> 000000013F131030 *or * * * * *eax,0FFFFFFFFh}
>
> 000000013F131033 *add * * * * rsp,38h
> 000000013F131037 *ret
>
> There is a huge difference. In x86 code the old implementation is used
> -> exception stack which pointer is held in [fs] segment register.
> In x64 code there is no exception stack anymore since the compiler uses
> a static table for stack unwinding, therefore no overhead if no
> exception is thrown.
> So the implementation should be comparable to Linux / Unix systems and
> compilers.
>
> Since I already mentioned that SEH doesn't add any overhead (if the
> compiler ignores SEH exceptions and doesn't need to track them if
> thrown) it's the compilers fault if there is a speed difference.
>
> And I don't think for example GCC under Windows uses a different
> exception model than under Linux ? Or does it ?


Interesting. I'll get the assembly for my tests and post it. I wonder
if I'm measuring random noise or something for my windowws AMD 64
test.

Also, your deduction is correct that I am using visual studios 2008. I
should have mentioned that. I wonder if 2010 makes a difference.
That'll likely have to wait until Monday or Tuesday though.
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
poping empty stack in visual c++ express - doesn't throw exception ? KRUNOPOPOVIC@YAHOO.COM C++ 11 03-05-2007 11:36 PM
findcontrol("PlaceHolderPrice") why why why why why why why why why why why Mr. SweatyFinger ASP .Net 2 12-02-2006 03:46 PM
JNI's throw new does not throw an exception yarona@m-sys.com Java 15 09-08-2005 08:36 AM
Try-catch not working - why does this throw an Exception? Jon Maz ASP .Net 7 10-25-2004 04:55 PM
Throw Exception Vs Throw New Exception Kerri ASP .Net 2 10-27-2003 02:13 PM



Advertisments