Applying the propper funciton pointer at runtime? (Mathfunctions)

Started by
9 comments, last by b2b3 17 years, 2 months ago
Hi to all. A couple of time now, I tryed to imagine how to assign a specific function pointer to the propper function itself. Here is the problem: When my application starts, the program makes an enumeration of the graphics hardware and the CPU. An important property this enumeration should reveal are the instruction sets. For example: Imagine that I implemented several mathematical ingedients for spacial calculation, e.g. vector math for diverent instruction sets. This means, I have a dot product implemented with SSE 3DNow, MMX, SSE2 ... Now the app detected that the CPU can handle SSE 3dNow and what ever... But I want to assign the vector function(pointer) to the propper implementation. I want to use the best one I can get. In the example above, this would be SSE. So I want that every usage of a vector product or the dot product refering/pointing to the propper implementation. And this would be the SSE implementation. I haven´t worked with smart pointers, but I could imagine, that this could be a solution to my problem?!(I know, smart pointers are imprtant. I will fill this gap soon...) But please tell my what I can do to get rid of this problem! Thanks a lot Alex
Advertisement
you are in fact on the right track, check out the following pseudo code:

//FIXME: return type & paramsvoid doStuffWithMMX() {// ...}void doStuffWithSSE() {// ...}void doStuffWithSSE2() {// ...}void doStuffWith3Dnow() {// ...}int main(){typedef void (*MY_FPTR) ();MY_FPTR pointers[]={&doStuffWithMMX,&doStuffWithSSE,&doStuffWithSSE2,&doStuffWith3Dnow}; //this could actually be a std::map<std::string, MY_FTPR>, to support syntax along the lines of: pointers["HAVE_SSE2"](...);//check for hardware support//call appropriate function}


BTW, see: http://www.newty.de/fpt/fpt.html
Hi, thanks for the fast response!

I understand.

But haven´t I to call pointer[...] every time than?

I think of someting like a general Name for the funciton like:
float DotProduct( CVector* )


But the function DotProduct() is assigned to the right function,
e.g DotProductSSE().

Thanks
Alex
The classic way of doing this goes like this:

class Math{    static float (*Length)(const Vector3 &);    // pointers for other functions    // Default "fallback" implementations    struct MathStd    {        static float Length(const Vector3 &x) { /* C implementation */ }    };    // SSE Implementations    struct MathSSE    {        static float Length(const Vector3 &x) { /* SSE implementation */ }    };    // 3DNow implementations    struct Math3DNow    {        static float Length(const Vector3 &x) { /* 3DNow! implementation */ }    };    // Assign correct functions to the appripriate pointers    static void Initialize(void)    {        if (HAS_SSE)        {            Length = MathSSE::Length;        }        else if (HAS_3DNOW)        {            Length = MathStd::Length;        }        else        {            Length = Math3DNow::Length;        }    }};


This way, you only need to call one method - Initialize - which will automatically set pointes based on the heuristic you like (you can even do a speed test to select fastest routine). This also leaves and option to use SSE/3DNow implementations directly (without need for the pointer) which may be even better since it allows for function inlining and various optimizations.
To use those functions, you can simply call Math::Length(x) etc.
Any particular reason you can't (or don't want to) determine the function to use at compile-time, instead? :s (e.g. by using ifdefs and conditional compilation)
Hi thanks.

@b2b3:
Thanks. This is exactly what I was talking about!!!
For an even good approach in fast math functions, this should fit my needs and whose of my application.

But one question comes up now.
b2b3, you are using static methods. Is is better to use static methods or singletons, for example. But I don´t know if a math singleton is the correct way...
No, static funcitons or a static classes sounds great.

@Zahlman:
Quote:(e.g. by using ifdefs and conditional compilation)

Before the idea with the pointers I had half implemented it with ifdefs, but than
I thought, this looks bad and it feels bad. How can I say it,...
I mean, when you have something like function pointers, why don't use then.
And there is a point of performace considerations(in my opinion, maybe I'm wrong. Maybe ifdef are faster than I think). I mean, consider the following problem I had:
You got a program which executes 1000 dot products in a sequence. So than, is it smart to ask the same question 1000 times, namly:"What instruction set do I have to use?"
E.g.:
I think this is what you mean:
float CVector::DotProduct( CVector &vc ){...#ifdef _SSE// do SSE stuff#elif _SSE2// do SSE2 stuff#elif _another expression//...#endif}


Is this what you mean?
I'm not a hardcore programming specialist, but in my eyes this looks much slower than the function pointer approach...
I mean, with the pointers you just have to ask your question once:"What instruction set so I have to use?" and this is in the initialize function b2b3 posted!

Greetings
Alex

#EDIT
Forget about the performance consideration. I don´t thought about the name "preprocessor directives" and the fact that the compiler is responsible for linking the propper functions. But the smartness point of criticism holds!!!
Maybe this is the reason why smart pointers are calles smart pointers?! ;)
#/EDIT
The #ifdefs are so called pre-processor directives. This means that they will only be evaluated BEFORE the actual compilation of your program.

if you have the code:

#ifdef _SSE// do SSE stuff#elif _SSE2// do SSE2 stuff#elif _another expression//...#endif


if you set _SSE2 for example the only code that will get to the compiler is:

// do SSE2 stuff

The small problem with this is a program compiled with SSE2 set will not run on a non-SSE2 CPU since the other code hasn't made it into the binary.
I'm sorry... I was to slow, see my edit. I got the problem...

Sorry for that
Quote:The small problem with this is a program compiled with SSE2 set will not run on a non-SSE2 CPU since the other code hasn't made it into the binary.

@Wc-duck(nice name:D)
Ok, thanks for this information. I wans't aware of this fact.

Alex
Quote:Original post by directNoob
I mean, when you have something like function pointers, why don't use then.


Because it costs you an extra pointer dereference for every single math operation. If you're going to use special instruction sets for extra speed doing serious number crunching, it's an important performance consideration.

Ideally you'd have separately written versions of all your bigger calculations (using, for example, templates, which are infinitely less ugly than the preprocessor) and try to work from there.

If you don't care about the speed, you might as well go on using the plain FPU.

This topic is closed to new replies.

Advertisement