mercredi 29 mars 2017

Cache locality with unique_ptr

I have a vector of custom classes (std::string just for example).

The vector is large and I iterate through often, so I rely on cache locality.

I also have one raw pointer which points at one of the vector elements.

Now is the trick:

The vector is sorted from time to time, so the raw pointer loose the actual pointed element value, and will point to some random element value.

Here is an example to illustrate the same:

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <memory>

using namespace std;

int main()
{

    vector<string> v = {"9","3", "8", "7", "6", "5", "1", "4", "2"};

    string* rs = &v[7]; //point to the 7th element

    for (size_t i = 0; i < v.size(); ++i)
        cerr << v[i];
    cerr << endl;
    cerr << "Referenced string: " << rs->c_str() << endl;

    cerr << "Sort ..." << endl;
    sort(v.begin(), v.end(), [](const string& a, const string& b)
    {
        if (a < b)
            return true;
        else
            return false;
    }
    );

    for (size_t i = 0; i < v.size(); ++i)
        cerr << v[i];
    cerr << endl;
    cerr << "Referenced string: " << rs->c_str() << endl;

    cin.get();
    return 0;

}

Output:

938765142
Referenced string before sort : 4
Sort ...
123456789
Referenced string after sort : 8

Since I wish the rs pointer to keep pointing to the 7th element value (which is 4) even after the sort, I came up with the following solution (vector of pointers):

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <memory>

using namespace std;

int main()
{


    vector<unique_ptr<string>> v;
    v.resize(9);
    v[0] = make_unique<string>("9");
    v[1] = make_unique<string>("3");
    v[2] = make_unique<string>("8");
    v[3] = make_unique<string>("7");
    v[4] = make_unique<string>("6");
    v[5] = make_unique<string>("5");
    v[6] = make_unique<string>("1");
    v[7] = make_unique<string>("4");
    v[8] = make_unique<string>("2");

    string* rs = v[7].get();        

    for (size_t i = 0; i < v.size(); ++i)
    cerr << v[i]->c_str();
    cerr << endl;
    cerr << "Referenced string before sort: " << rs->c_str() << endl;


    cerr << "Sort ..." << endl;
    sort(v.begin(), v.end(), [](const unique_ptr<string>& a, const unique_ptr<string>& b)
    {
    if (*a < *b)
    return true;
    else
    return false;
    }
    );



    for (size_t i = 0; i < v.size(); ++i)
    cerr << v[i]->c_str();
    cerr << endl;
    cerr << "Referenced string after sort: " << rs->c_str() << endl;


    cin.get();
    return 0;

}

Output:

938765142
Referenced string before sort: 4
Sort ...
123456789
Referenced string after sort: 4

While this latter solution works, there is a price: I have lost the cache locality of my vector, since I store pointers in it, rather than the actual objects.

Is there a way to maintain cache locality (e.g.: store my actual objects in the vector), and somehow manage to rs pointer to keep track where its pointed value wander around due to the sorts? Or from the other perspective, is there a way to achieve cache locality with the vector of pointers?

Aucun commentaire:

Enregistrer un commentaire