473,379 Members | 1,187 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,379 software developers and data experts.

unexpected abstraction penalty in C++

class c {
int x;
public:
inline c() : x(0) {}
inline c(int i) { x = i; }
inline operator const int& () const { return x; }
inline operator int& () { return x; }
};
If I use objects of type `c' as if they were int's, I see about 1.5x
slow-down compared to using regular int's (timed using GCC-3.3.4 -O3). Is
it reasonable to expect modern compilers to optimize this, so that there is
no abstraction penalty?
Jul 23 '05 #1
8 1750
* alex goldman:
class c {
int x;
public:
inline c() : x(0) {}
inline c(int i) { x = i; }
inline operator const int& () const { return x; }
inline operator int& () { return x; }
};
'inline' is superflous here, because the functions are defined in-class
which makes the automatically 'inline'.

Second, 'inline' is at best only a hint.

To direct your compiler to optimize for speed, use your compiler's options
and/or non-standard language extensions.

If I use objects of type `c' as if they were int's, I see about 1.5x
slow-down compared to using regular int's (timed using GCC-3.3.4 -O3). Is
it reasonable to expect modern compilers to optimize this, so that there is
no abstraction penalty?


Don't know.

With the following main program:

int main( int nArgs, char* arg[] )
{
int forceCode = atoi( arg[1] );

int x = 0x1234 + forceCode;
x += 0x1111;
std::cout << x << std::endl;

c y = 0x1234 + forceCode;
y += 0x1111;
std::cout << y << std::endl;
}

Visual C++ 7.1, optimize for speed or full optimization, emits a
single instruction for the initialization of and addition to 'x',

; int x = 0x1234 + forceCode;
; x += 0x1111;
lea edx,[esi+2345h]

in contrast to two instruction for the code using your class, 'y',

; c y = 0x1234 + forceCode;
lea eax,[esi+1234h]
; y += 0x1111;
add eax,1111h

I don't understand why the compiler can see the optimization for 'x'
but not for 'y', having gone so far as to represent them identically.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Jul 23 '05 #2
Alf P. Steinbach wrote:
* alex goldman:
class c {
int x;
public:
inline c() : x(0) {}
inline c(int i) { x = i; }
inline operator const int& () const { return x; }
inline operator int& () { return x; }
};

'inline' is superflous here, because the functions are defined in-class
which makes the automatically 'inline'.

Second, 'inline' is at best only a hint.

To direct your compiler to optimize for speed, use your compiler's options
and/or non-standard language extensions.
If I use objects of type `c' as if they were int's, I see about 1.5x
slow-down compared to using regular int's (timed using GCC-3.3.4 -O3). Is
it reasonable to expect modern compilers to optimize this, so that there is
no abstraction penalty?

Don't know.

With the following main program:

int main( int nArgs, char* arg[] )
{
int forceCode = atoi( arg[1] );

int x = 0x1234 + forceCode;
x += 0x1111;
std::cout << x << std::endl;

c y = 0x1234 + forceCode;
y += 0x1111;
std::cout << y << std::endl;
}

Visual C++ 7.1, optimize for speed or full optimization, emits a
single instruction for the initialization of and addition to 'x',

; int x = 0x1234 + forceCode;
; x += 0x1111;
lea edx,[esi+2345h]

in contrast to two instruction for the code using your class, 'y',

; c y = 0x1234 + forceCode;
lea eax,[esi+1234h]
; y += 0x1111;
add eax,1111h

I don't understand why the compiler can see the optimization for 'x'
but not for 'y', having gone so far as to represent them identically.


IMHO it's related to the point of time when the compiler's constant
folding kick in. I'd expect it to operate primarily on POD leaving out
objects (even though they might act the way the OP implements them). I
suppose this could impose overhead in the dataflow analysis which
probably won't pay off in most cases.

Cheers
Chris
Jul 23 '05 #3
alex goldman wrote:
class c {
int x;
public:
inline c() : x(0) {}
inline c(int i) { x = i; }
inline operator const int& () const { return x; }
inline operator int& () { return x; }
};
If I use objects of type `c' as if they were int's, I see about 1.5x
slow-down compared to using regular int's (timed using GCC-3.3.4 -O3). Is
it reasonable to expect modern compilers to optimize this, so that there is
no abstraction penalty?


Well you don't show us your code that actually uses the class, but there
are a few differences between
int [100];
and
c [100];

The first is that with an array of ints (or any POD) the default
initialization is either static or omitted. In your case,
a dynamic initialization always occurs.
Jul 23 '05 #4
On 2005-05-29, alex goldman <he***@spamm.er> wrote:
class c {
int x;
public:
inline c() : x(0) {}
inline c(int i) { x = i; }
inline operator const int& () const { return x; }
inline operator int& () { return x; }
};
If I use objects of type `c' as if they were int's, I see about 1.5x
slow-down compared to using regular int's (timed using GCC-3.3.4 -O3). Is
it reasonable to expect modern compilers to optimize this, so that there is
no abstraction penalty?


Does it make any difference if you change that to operator int() ?

Cheers,
--
Donovan Rebbechi
http://pegasus.rutgers.edu/~elflord/
Jul 23 '05 #5
Ron Natalie wrote:
In your case, a dynamic initialization always occurs.


That's a very good point. I removed the initialization just to be sure, but
it didn't make a difference for speed. It's still 2:3 for int vs the class.

Here's one of the benchmarks I ran:

#include <iostream>

class c {
int x;
public:
c() {}
c(int i) { x = i; }
operator const int& () const { return x; }
operator int& () { return x; }
};

#define LOOP1(i, n) for((i) = -(n); (i) <= (n); ++(i))
#define LOOP(i, j, k, n, a) \
a = 0; \
LOOP1(i, n) LOOP1(j, n) LOOP1(k, n) \
a += k + j; return a

int f_i(int n) {
int i, j, k, acc;
LOOP(i, j, k, n, acc);
}

int f_c(c n) {
c i, j, k, acc;
LOOP(i, j, k, n, acc);
}

int main() {
// std::cout << f_i(1000) << '\n';
std::cout << f_c(1000) << '\n';
}

Jul 23 '05 #6
Donovan Rebbechi wrote:

Does it make any difference if you change that to operator int() ?


I can't change the non-const operator to int (), but changing the const one
makes no difference.
Jul 23 '05 #7
alex goldman wrote:
Ron Natalie wrote:

In your case, a dynamic initialization always occurs.

That's a very good point. I removed the initialization just to be sure, but
it didn't make a difference for speed. It's still 2:3 for int vs the class.

You might try adding operator += and ++ to see what difference that
makes, or alternatively a operator=(int).
Jul 23 '05 #8
I wrote a more comprehensive test of various abstraction penalties in C++.
Here's what I get on P4 with GCC-3.3.4 -O3 (agressive optimization &
inlining):

$ time ./a.out

f_int(1000) : 9.240s
f_class1(1000) : 13.890s
f_class2(1000) : 19.510s
f_method(1000) : 13.850s
f_macro(1000) : 9.320s
f<int>(1000) : 9.240s
f<c>(1000) : 19.490s
f_get1(1000) : 13.850s
f_get2(1000) : 32.660s

real 2m21.092s
user 2m20.928s
sys 0m0.133s
Lessons learned:
* regular accessors (getters & setters) didn't help
* very minor things can confuse the optimizer (class1 vs class2)

I'm be curious to know how other CPUs/compilers do. Program text follows.

#include <iostream>
#include <ctime>
#include <iomanip>

using namespace std;

double time() { return double(clock()) / CLOCKS_PER_SEC; }

#define TIME_INC(e, res) { \
double t1 = time(); \
(res) += (e); \
double t2 = time(); \
cout << setw(15) << #e << " : " \
<< setw(7) << fixed << setprecision(3) \
<< t2 - t1 << "s" << endl; \
}

class c {
public:
int x;
c() {}
c(int i) : x(i) {}
operator const int& () const { return x; }
operator int& () { return x; }
const int& i() const { return x; }
int& i() { return x; }
int get() const { return x; }
void set(int i) { x = i; }
};

#define LOOP1(i, n) for((i) = -(n); (i) <= (n); ++(i))
#define LOOP(i, j, k, n, a) \
a = 0; \
LOOP1(i, n) LOOP1(j, n) LOOP1(k, n) \
a += k + j; return a

int f_int(int n) {
int i, j, k, acc;
LOOP(i, j, k, n, acc);
}

int f_class1(c n) {
c i, j, k, acc;
LOOP(i, j, k, n, acc);
}

// the return type is different from the above

c f_class2(c n) {
c i, j, k, acc;
LOOP(i, j, k, n, acc);
}

int f_method(c n) {
c i, j, k, acc;
LOOP(i.i(), j.i(), k.i(), n.i(), acc.i());
}

// very similar, but 1.5x faster!

#define I(e) (e).x
int f_macro(c n) {
c i, j, k, acc;
LOOP(I(i), I(j), I(k), I(n), I(acc));
}

template<class T>
T f(T n) {
T i, j, k, acc;
LOOP(i, j, k, n, acc);
}

int f_get1(c n) {
c i, j, k, acc = 0;
for(i.set(-n.get()); i.get() <= n.get(); i.set(i.get() + 1))
for(j.set(-n.get()); j.get() <= n.get(); j.set(j.get() + 1))
for(k.set(-n.get()); k.get() <= n.get(); k.set(k.get() + 1))
acc.set(acc.get() + k.get() + j.get());
return acc;
}

// the return type makes a big difference:

c f_get2(c n) {
c i, j, k, acc = 0;
for(i.set(-n.get()); i.get() <= n.get(); i.set(i.get() + 1))
for(j.set(-n.get()); j.get() <= n.get(); j.set(j.get() + 1))
for(k.set(-n.get()); k.get() <= n.get(); k.set(k.get() + 1))
acc.set(acc.get() + k.get() + j.get());
return acc;
}

#define TIME(e) TIME_INC(e(1000), dummy)

int main() {
int dummy = 0;
TIME(f_int);
TIME(f_class1)
TIME(f_class2)
TIME(f_method);
TIME(f_macro);
TIME(f<int>);
TIME(f<c>);
TIME(f_get1);
TIME(f_get2);
return dummy;
}

Jul 23 '05 #9

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: Michael Andersson | last post by:
Hi! Does the use of exception handling induce a performance penalty during the execution of non exception handling code? Regards, /Michael
0
by: Ed | last post by:
------=_NextPart_000_001D_01C34FCC.1D2B5E50 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable There seems to be a significant penalty imposed by the...
9
by: A. Saksena | last post by:
Hi, Do anybody have an idea of the performance penalty while using exception handling (specially with g++) Abhishek
21
by: ambika | last post by:
Hello, I have a very basic doubt. Why is C called a structured programming language??why structured? C++ is called a Object Oriented language 'cos it obeys the OOP's concepts..Why is C called a...
6
by: Mark Broadbent | last post by:
this might sound like an obvious question but I have found that usually these two evolve at the same time. One of the biggest reasons for creating the abstraction in the first place (in my...
2
by: Peter Bär | last post by:
A Question to the C#/.Net Gods of this forum: are there performance penalties when i compile (C#, FW1.1, ASP.NET, Studio2003) a central baseclass in a different assembly than all the derived...
1
by: rickycornell | last post by:
Greetings All, On past projects in PHP4 I had always just written my own libraries to deal with database interaction. Somehow I was operating in the dark that there were all these database...
2
by: subramanian100in | last post by:
Is my following understanding correct ? Data abstraction means providing the interface - that is, the set of functions that can be called by the user of a class. Information hiding means...
8
by: Ivan S | last post by:
What are your recommendations for lightweight database abstraction library (Oracle/MySQL)? I prefer OOP. :) Tnx, Ivan.
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
by: ryjfgjl | last post by:
In our work, we often need to import Excel data into databases (such as MySQL, SQL Server, Oracle) for data analysis and processing. Usually, we use database tools like Navicat or the Excel import...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.