Re: Why do some code bases don't use exceptions?
On Nov 27, 11:53 pm, Ian Collins <ian-n...@hotmail.com> wrote:
Joshua Maurice wrote:
On Nov 20, 8:09 pm, White Wolf <wo...@freemail.hu> wrote:
Paavo Helde wrote:
Regarding Google, maybe they might have banned exceptions because of =
the
run-time overhead (just guessing). Throwing exceptions is notoriously
slow, so they should not be used unless something goes very wrong - b=
ut
for a 24/7 service like Google nothing should go wrong ever, so no ne=
ed
for exceptions, right?
Notoriously slow is a pretty vague statement. Exceptions do not slo=
wer
the speed of the code unless they are thrown. So your choices are: =
slow
your happy code (that runs a billion times) by inserting hundreds of i=
f
statements into it to propagate return codes up the call stack to the
handler, or make returning 1000 times slower 3 times a day... It is=
a
no brainer to me.
Sadly no. That was the intent. However, on half of all of the unix-
like environments available for me to test on, including Solaris, AIX,
HPUX, Linux, and for the common windows platforms, win32, win64,
exceptions add overhead even when not thrown, some worse than others.
Windows, for example, implements C++ exceptions on top of their
structured exception handling, which means that you're paying a
penalty every time you enter a try block. For a particularly contrived
test I once wrote, I got performance slowdowns of up to ~1.5x slower
than the version which used error return codes on one platform.
Not that I'm trying to say use exceptions or don't in this post. Just
understand their practical costs and don't repeat (wishful)
misinformation.
How have you measured? All my tests on Solaris with gcc and Sun CC hav=
e
shown the normal execution path to be faster with exceptions than
testing return values.
My tests on Sun 64 with gcc also show that the implementation uses the
"correct" aka "good" aka table approach to implementing exceptions,
resulting in (near) zero overhead. (Sun on x86 is another story.)
Here are my tests first (which I did a year or two ago), giving a
basic description of the platform and the compiler options used. After
will be the code which I ran. Note that I did some marginal care to
make sure I wasn't seeing caching or preloading, but I don't claim
that these numbers are fullproof or otherwise reliable beyond a very
basic eyeball level. Specifically, I wouldn't use these numbers too
much beyond to answer "Does exception presence without ever throwing
cause overhead in C++ on X implementation?"
Note that I ran each test with input numbers to get a wallclock time
of ~30 seconds. Note that the test suite runs the tests in a (pseudo-)
random order to avoid some weird caching or optimization effects I was
seeing. (I think). Note that I do a single run of the individual test
before starting timing to try and minimize artificial caches misses.
Again, I do not claim that these tests are very thorough or robust or
even correct. I think they are.
Note that the compilers I'm using are somewhat old as well. I'm merely
using what my company used to compile our product on these platforms
several years ago. I do hope I used the right compiler options. (I did
some quick google research to determine the command line options.)
The x### is the factor comparing the slower of the two tests "virtual
exception" or "virtual fake try block" vs the test "virtual return
code".
Windows XP Pro 32
using visual studios 2003 32-bit compiler, standard release compiler
options.
Exception using code x1.04
AIX 32
% uname -a
AIX bigpine 2 5 0003DFC3D600
% xlC_r -V
C for AIX Compiler, Version 6
[Rest omitted]
% xlC_r -O4 -qnotempinc foo.cpp
Exception using code x0.94
(Yes. I have no clue why it's smaller, but it was a reproducible
number.)
AIX 64
% uname -a
AIX waco 2 5 00CAEAEA4C00
% xlC_r -V
C for AIX Compiler, Version 6
[Rest omitted]
% xlC_r -O4 -qnotempinc -q64 foo.cpp
Exception using code x1.48
HP 32
[9] % uname -a
HP-UX nimbus B.11.11 U 9000/800 2328359749 unlimited-user license
[10] % aCC -V
aCC: HP ANSI C++ B3910B A.03.73
[11] % aCC -AA -mt -z -ext -Wc,-ansi_for_scope,on +O3 foo.cpp
Exception using code x1.16
HP 64
[9] % uname -a
HP-UX cygnus B.11.11 U 9000/800 2328359751 unlimited-user license
[10] % aCC -V
aCC: HP ANSI C++ B3910B A.03.73
[11] % aCC -AA -mt -z -ext -Wc,-ansi_for_scope,on +O3 +DD64 foo.cpp
Exception using code x1.46
hp ipf
[11] % uname -a
HP-UX texan B.11.23 U ia64 0622264057 unlimited-user license
[12] % aCC -V
aCC: HP aC++/ANSI C B3910B A.06.05 [Jul 25 2005]
[13] % aCC -AA -mt -z -ext -Wc,-ansi_for_scope,on +O3 +DD64
+DSitanium2 foo.cpp
Exception using code x1.27
lin 32
[jmaurice@davey ~/test]$ uname -a
Linux davey.informatica.com 2.6.9-5.ELsmp #1 SMP Wed Jan 5 19:30:39
EST 2005 i686 i686 i386 GNU/Linux
[jmaurice@davey ~/test]$ g++ --version
g++ (GCC) 3.4.3 20050227 (Red Hat 3.4.3-22.1)
[Rest omitted]
[jmaurice@nagara ~/test]$ g++ -O3 foo.cpp
Exception using code x1.0
lin 64
[jmaurice@nagara ~/test]$ uname -a
Linux nagara 2.6.9-11.ELsmp #1 SMP Fri May 20 18:25:30 EDT 2005 x86_64
x86_64 x86_64 GNU/Linux
[jmaurice@nagara ~/test]$ g++ --version
g++ (GCC) 3.4.3 20050227 (Red Hat 3.4.3-22.1)
[Rest omitted]
[jmaurice@nagara ~/test]$ g++ -O3 foo.cpp
Exception using code x1.0
lin IA 46
[jmaurice@seraph ~/test]$ g++ --version
g++ (GCC) 3.3.1
[Rest omitted]
[jmaurice@seraph ~/test]$ g++ -O3 foo.cpp
Exception using code x1.0
sun 64
[test] uname -a
SunOS mosquito 5.8 Generic_108528-29 sun4u sparc SUNW,Sun-Fire-V440
[test] CC -V
CC: Sun C++ 5.5 2003/03/12
[test] CC -O4 +w +w2 -mt -features=extensions -xarch=generic64 -
xtarget=generic64 foo.cpp
Exception using code x1.0
sun x86
[test] uname -a
SunOS coyote 5.10 Generic_Patch_118844-30 i86pc i386 i86pc
[test] CC -V
CC: Sun C++ 5.8 2005/10/13
[test] CC -O4 +w +w2 -mt -features=extensions foo.cpp
Exception using code x1.05
////
//Start test
#include <cstdlib>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
enum ReturnCodeT { Success = 1, Failure = 2 };
class TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum) = 0;
virtual ReturnCodeT virtualReturnCode(int a, int b, int targetSum)
= 0;
ReturnCodeT inlineableReturnCode(int a, int b, int targetSum)
{ if (a + b == targetSum)
return Failure;
return Success;
}
};
inline ReturnCodeT globalInlineableReturnCode(int a, int b, int
targetSum)
{ if (a + b == targetSum)
return Failure;
return Success;
}
class TestImpl : public TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum)
{ if (a + b == targetSum)
throw 1;
}
virtual ReturnCodeT virtualReturnCode(int a, int b, int targetSum)
{ if (a + b == targetSum)
return Failure;
return Success;
}
};
class TestImpl2 : public TestInterface
{
public:
virtual void virtualCanThrow(int a, int b, int targetSum)
{ cout << "XXX" << endl;
}
virtual ReturnCodeT virtualReturnCode(int a, int b, int targetSum)
{ cout << "XXX" << endl;
return Success;
}
};
void testInlineableMemberReturnCode(TestInterface *& x, int arg1, int
arg2)
{ { vector<int> vec;
for (int i=0; i<arg1; ++i)
{ for (int j=0; j<arg1; ++j)
{ //vec.push_back(i+j);
if (Failure == x->inlineableReturnCode(i, j, arg2+3))
{ cout << "inlineable member return code, returned
failure" << endl;
x = new TestImpl2();
}
}
}
if (vec.size() && vec.back() == arg2+3)
{ cout << "inlineable member return code, vector size
comparison true" << endl;
x = new TestImpl2();
}
}
}
void testInlineableGlobalReturnCode(TestInterface *& x, int arg1, int
arg2)
{ { vector<int> vec;
for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
{ //vec.push_back(i+j);
if (Failure == globalInlineableReturnCode(i, j,
arg2+4))
cout << "inlineable global return code, returned
failure" << endl;
}
if (vec.size() && vec.back() == arg2+4)
cout << "inlineable global return code, vector size
comparison true" << endl;
}
}
void testVirtualReturnCodeFakeTry(TestInterface *& x, int arg1, int
arg2)
{ { vector<int> vec;
for (int i=0; i<arg1; ++i)
{ for (int j=0; j<arg1; ++j)
{ //vec.push_back(i+j);
try
{ if (Failure == x->virtualReturnCode(i, j, arg2+5))
{ cout << "virtual return code with fake try,
returned failure" << endl;
x = new TestImpl2();
}
} catch (...)
{ cout << "ERROR impossible exception caught" <<
endl;
x = new TestImpl2();
}
}
}
if (vec.size() && vec.back() == arg2+5)
{ cout << "virtual return code with fake try, vector size
comparison true" << endl;
x = new TestImpl2();
}
}
}
void testVirtualReturnCode(TestInterface *& x, int arg1, int arg2)
{ { vector<int> vec;
for (int i=0; i<arg1; ++i)
{ for (int j=0; j<arg1; ++j)
{ //vec.push_back(i+j);
if (Failure == x->virtualReturnCode(i, j, arg2+6))
{ cout << "virtual return code, returned failure" <<
endl;
x = new TestImpl2();
}
}
}
if (vec.size() && vec.back() == arg2+6)
{ cout << "virtual return code, vector size comparison true"
<< endl;
x = new TestImpl2();
}
}
}
void testVirtualException(TestInterface *& x, int arg1, int arg2)
{ { vector<int> vec;
for (int i=0; i<arg1; ++i)
{ for (int j=0; j<arg1; ++j)
{ //vec.push_back(i+j);
try
{ x->virtualCanThrow(i, j, arg2+7);
} catch (int & )
{ cout << "virtual exception, exception caught" <<
endl;
x = new TestImpl2();
}
}
}
if (vec.size() && vec.back() == arg2+7)
{ cout << "virtual exception, vector size comparison true"
<< endl;
x = new TestImpl2();
}
}
}
void testManuallyInlinedReturnCode(TestInterface *& x, int arg1, int
arg2)
{ { vector<int> vec;
for (int i=0; i<arg1; ++i)
{ for (int j=0; j<arg1; ++j)
{ //vec.push_back(i+j);
ReturnCodeT retVal;
if (i + j == arg2+8)
retVal = Failure;
else
retVal = Success;
if (retVal == Failure)
{ cout << "manually inlined return code, failure
'returned'" << endl;
x = new TestImpl2();
}
}
}
if (vec.size() && vec.back() == arg2+8)
{ cout << "manually inlined return code, vector size
comparison true" << endl;
x = new TestImpl2();
}
}
}
void testManuallyInlinedOptimizedReturnCode(TestInterface *& x, int
arg1, int arg2)
{ { vector<int> vec;
for (int i=0; i<arg1; ++i)
for (int j=0; j<arg1; ++j)
{ //vec.push_back(i+j);
if (i + j == arg2+9)
{ cout << "manually inlined and optimized return
code, failure 'returned'" << endl;
x = new TestImpl2();
}
}
if (vec.size() && vec.back() == arg2+9)
{ cout << "manually inlined and optimized return code,
vector size comparison true" << endl;
x = new TestImpl2();
}
}
}
int main(int argc, char ** argv)
{
if (argc != 3 && argc != 4)
return 1;
int arg1;
if (! (stringstream(argv[1]) >> arg1))
return 1;
int arg2;
if (! (stringstream(argv[2]) >> arg2))
return 1;
TestInterface * x;
if (argc == 3)
x = new TestImpl();
else
x = new TestImpl2();
vector<clock_t> retVal;
typedef void (*TestFuncType)(TestInterface *&, int, int);
vector<pair<string, TestFuncType> > remainingTests;
remainingTests.push_back(make_pair(string("Inlineable Member
Return Code"), & testInlineableMemberReturnCode));
remainingTests.push_back(make_pair(string("Inlineable Global
Return Code"), & testInlineableGlobalReturnCode));
remainingTests.push_back(make_pair(string("Virtual Return Code
Fake Try"), & testVirtualReturnCodeFakeTry));
remainingTests.push_back(make_pair(string("Virtual Return
Code"), & testVirtualReturnCode));
remainingTests.push_back(make_pair(string("Virtual
Exception"), & testVirtualException));
remainingTests.push_back(make_pair(string("Manually Inlined Return
Code"), & testManuallyInlinedReturnCode));
remainingTests.push_back(make_pair(string("Manually Inlined
Optimized Return Code"), & testManuallyInlinedOptimizedReturnCode));
srand(time(0));
while (remainingTests.size())
{
int index = rand() % remainingTests.size();
pair<string, TestFuncType> thisTest = remainingTests[index];
remainingTests.erase(remainingTests.begin() + index);
clock_t t0 = clock();
(*thisTest.second)(x, 1, -10);
clock_t t1 = clock();
(*thisTest.second)(x, arg1, arg2);
clock_t t2 = clock();
cout << setw(40) << thisTest.first << " : " << double(t2 -
t1) / double(CLOCKS_PER_SEC) << endl;
}
}