Calling C functions from C++ is very slow

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Calling C functions from C++ is very slow

Post by kemosparc »

Hi,

I am building my own stdc and stdc++ libraries for my own kernel. Trying to do some optimization to enhance performance I noticed that calling C functions from C++ in the user mode is very slow!

I was able to isolate the problem in a separate case. I have the ctype library in the stdc:

Code: Select all

#ifndef CTYPE_H_
#define CTYPE_H_


#define	_U	0x01
#define	_L	0x02
#define	_N	0x04
#define	_S	0x08
#define	_P	0x10
#define	_C	0x20
#define	_X	0x40
#define	_B	0x80

extern "C" {
    unsigned char	isalnum(unsigned char);
    unsigned char	isalpha(unsigned char);
    unsigned char	iscntrl(unsigned char);
    unsigned char	isdigit(unsigned char);
    unsigned char	isgraph(unsigned char);
    unsigned char	islower(unsigned char);
    unsigned char	isprint(unsigned char);
    unsigned char	ispunct(unsigned char);
    unsigned char	isspace(unsigned char);
    unsigned char	isupper(unsigned char);
    unsigned char	isxdigit(unsigned char);
    unsigned char	tolower(unsigned char);
    unsigned char	toupper(unsigned char);
}
#endif

Code: Select all


#include <ctype.h>

unsigned char isalnum(unsigned char _c)
{
    return (_c > 47 && _c < 58) || (_c > 64 && _c < 91) || (_c > 96 && _c < 123);
}

unsigned char isalpha(unsigned char _c)
{
    return (_c > 64 && _c < 91) || (_c > 96 && _c < 123);
}

 unsigned char iscntrl(unsigned char _c)
{
    return (_c >= 0 && _c < 32) || (_c == 127);
}

 unsigned char isdigit(unsigned char _c)
{
    return (_c > 47 && _c < 58);
}

 unsigned char isgraph(unsigned char _c)
{
    return (_c > 32 && _c < 127);
}

 unsigned char islower(unsigned char _c)
{
    return (_c > 96 && _c < 123);
}

 unsigned char isprint(unsigned char _c)
{
    return (_c > 31 && _c < 127);
}

 unsigned char ispunct(unsigned char _c)
{
    return (_c > 32 && _c < 48) || (_c > 57 && _c < 65) || (_c > 90 && _c < 97) || (_c > 122 && _c < 127);
}

 unsigned char isspace(unsigned char _c)
{
    return ((_c > 8 && _c < 14) || (_c == 32));
}

 unsigned char isupper(unsigned char _c)
{
    return  (_c > 64 && _c < 91);
}

 unsigned char isxdigit(unsigned char _c)
{
    return (_c > 47 && _c < 58) || (_c > 64 && _c < 71) || (_c > 96 && _c < 103);
}

 unsigned char tolower(unsigned char _c)
{
    return (_c > 64 && _c < 91) ? _c + 32 : _c;
}

 unsigned char toupper(unsigned char _c)
{
    return (_c > 96 && _c < 123) ? _c - 32 : _c;
}
I also have implemented the cctype library like this:

Code: Select all

#ifndef CCTYPE_H_
#define CCTYPE_H_
#include <ctype.h>

namespace std{

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isalnum(int c)
    {
        return ::isalnum(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isalpha(int c)
    {
        return ::isalpha(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int iscntrl(int c)
    {
        return ::iscntrl(c);
    }
    
    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isdigit(int c)
    {
        return ::isdigit(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isgraph(int c)
    {
        return ::isgraph(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int islower(int c)
    {
        return ::islower(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isprint(int c)
    {
        return ::isprint(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int ispunct(int c)
    {
        return ::ispunct(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isspace(int c)
    {
        return ::isspace (c);
    }
    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isupper(int c)
    {
        return ::isupper(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isxdigit(int c)
    {
        return ::isxdigit(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int tolower(int c)
    {
            return ::tolower(c);
    }

    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int toupper(int c)
    {
            return ::toupper(c);
    }

};
#endif

I have a piece of code that uses std::isspace in a loop extensively for 92 seconds . and when I modify the std:isspace function like below the same code executes in 64 seconds.

Code: Select all


    inline __attribute__ ((__visibility__("hidden"))) __attribute__ ((__always_inline__))
    int isspace(int c)
    {
       return ((c > 8 && c < 14) || (c == 32));
    }

Any idea what could be the problem I think that 30% difference is too much for just a function call!!

Thanks,
Karim.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Calling C functions from C++ is very slow

Post by kzinti »

I'm guessing that your custom version is getting inlined in your loop where ::isspace() is not.

So basically your custom version is not actually making a call to a function, but using ::isspace() does require making function calls.

You'd have to check the compiler's output to make sure (i.e. disassemble your loop).

If you know that and are basically asking "why is it this slow to make these function calls", well... function calls are not free and I don't think there is much you can do about it. Take a step back and ask yourself if optimizing ::isspace() is the right thing... perhaps there is some other optimization you could do at a higher level so that you don't have to call ::isspace() as much.
kemosparc
Member
Member
Posts: 207
Joined: Tue Oct 29, 2013 1:13 pm

Re: Calling C functions from C++ is very slow

Post by kemosparc »

Thanks for the reply,

In that my back is really to the wall. In the process of testing my stdc++ implementation I am getting already existing applications to run them and test with. This ready made application I am using uses std::isspace.

Thanks,
Karim.
User avatar
Schol-R-LEA
Member
Member
Posts: 1925
Joined: Fri Oct 27, 2006 9:42 am
Location: Athens, GA, USA

Re: Calling C functions from C++ is very slow

Post by Schol-R-LEA »

Odd, given that the issue seems to be inlining (or lack of such), I would have expected the opposite; by which I mean, that it is using the C version, which - unlike the std::isspace() in your cctype header - isn't inlined.

Which C standard are you compiling by? C99 and later all support inlined functions, IIUC. Now, C inline (as of C99, I don't know if it got changed later off-hand) is somewhat different from C++ inline, in that it is an optimization hint rather than a strict directive, but given that you are already using the GCC __attribute__ extension for your C++ header, it would follow that you should be free to use that for the same purpose in your C header.

So the next thing to try, if you haven't already, is simply to move the C version from the source file back into the header as inline functions. You might even be able to consolidate the two versions into one header with suitable preprocessor switches and a dummy cctype which simply includes in ctype.h with the necessary flag to have it compile for C++, though I honestly wouldn't recommend it.
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
Ordo OS Project
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.
Post Reply