Wednesday, May 15, 2013

There Must Be Some Use For...

There's lots of obscure features in C++. One of which is "pointers to Class Members". I remember looking at some high performance code once that used this bizarre feature- I wondered about why this would be preferable. I'm not stating that this is a fair test but it definitely opens room for interpretation on what could be faster:

main1.cpp:

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

enum
{
    SAMPLE_SIZE = 65536
};

class A
{
    public:
        A() : description(), id(rand() % 32767) { }

        std::string description;
        int id;

};


std::ostream& operator<<(std::ostream& out, A const& a)
{
    out << "A(" << a.id << ")";
    return out;
}


int main(int argc, char** argv)
{
    std::vector<A> elements;
    elements.reserve(SAMPLE_SIZE);
    for(size_t i=0; i<SAMPLE_SIZE; ++i)
    {
        elements.push_back(A());
    }

    std::ostream_iterator<A> iter(std::cout, "\n");
    std::copy( elements.begin(), elements.end(), iter);   
}


And I'm going to compare against:

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>

enum
{
    SAMPLE_SIZE = 65536
};

class A
{
    public:
        A() : description(), id(rand() % 32767) { }

        std::string description;
        int id;

};


std::ostream& operator<<(std::ostream& out, A const& a)
{
    out << "A(" << a.id << ")";
    return out;
}


int main(int argc, char** argv)
{
    std::vector<A> elements;
    elements.reserve(SAMPLE_SIZE);
    for(size_t i=0; i<SAMPLE_SIZE; ++i)
    {
        elements.push_back(A());
    }

    int A::*idPtr;
    idPtr = &A::id; // Just a numeric value.

    for(size_t i=0; i<SAMPLE_SIZE; ++i)
    {
        std::cout << "A(" << elements[i].*idPtr << ")\n";
    }   
}


The obvious difference is how we are iterating over the container. In main2.cpp I'm using the pointer to class member to access the value whereas the first program uses iterators to perform the outputs. Both solutions are probably acceptable- but I'm seeing better results for main2.cpp! At first I kept the sample size low (under 32767 numbers). For lower number of computations the numbers were identical. Once I upped the sample size to a higher number I've started to see a discrepancy.

The result:

main1: 
real         0m0.021s
user         0m.016s
sys          0m0.000s

main2:

real         0m0.019s

user         0m0.016s
sys          0m0.000s

Like I disclosed before, I'm not sure if this is a fair test. Commentary would be nice on this post. The compiler used was gcc 4.7 on Ubuntu running in a virtual machine. I may consider researching this topic further to see if the performance improvements are consistent.


No comments:

Post a Comment