使用unique_ptr缓存局部性

时间:2017-03-29 20:26:44

标签: c++ c++11 pointers caching vector

我有一个自定义类的向量(例如std :: string)。

向量很大,我经常迭代,所以我依赖缓存局部性。

我还有一个指向其中一个向量元素的原始指针。

现在就是诀窍:

向量是不时排序的,因此原始指针会松散实际的指向元素值,并指向一些随机元素值。

以下是一个说明相同的例子:

#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;

}

输出:

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

由于我希望 rs 指针在排序后仍然指向第7个元素值(即4),我想出了以下解决方案(指针向量):

#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;

}

输出:

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

虽然后一种解决方案有效,但有一个代价:我丢失了向量的缓存局部性,因为我在其中存储了指针,而不是实际的对象。

有没有办法维护缓存局部性(例如:将我的实际对象存储在向量中),并以某种方式管理 rs 指针以跟踪由于排序而导致其指向值四处漂移的位置? 或者从另一个角度来看,有没有办法用指针向量实现缓存局部性?

来自Pubby的解决方案,谢谢!:

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

using namespace std;

int main()
{

    vector<string> data = { "d","e", "f", "g", "i", "b", "c", "a", "h" };
    vector<int> indexes = {0,1,2,3,4,5,6,7,8};


    int si = 6;

    for (size_t i = 0; i < indexes.size(); ++i)
        cerr << indexes[i];
    cerr << endl;
    for (size_t i = 0; i < indexes.size(); ++i)
        cerr << data[indexes[i]];
    cerr << endl;
    cerr << "Referenced string before sort: " << data[si] << endl;

    cerr << "Sort ..." << endl;
    sort(indexes.begin(), indexes.end(), [&](const int a, const int b)
    {
        return data[a] < data[b];
    }
    );

    for (size_t i = 0; i < indexes.size(); ++i)
        cerr << indexes[i];
    cerr << endl;
    for (size_t i = 0; i < indexes.size(); ++i)
        cerr << data[indexes[i]];
    cerr << endl;
    cerr << "Referenced string after sort: " << data[si] << endl;

    cin.get();
    return 0;

}

2 个答案:

答案 0 :(得分:7)

您可以通过将字符串存储在不会更改的向量中来增加位置,然后将指针/索引的向量存储到这些字符串中。

像这样:

vector<string> data = {"9","3", "8", "7", "6", "5", "1", "4", "2"};
vector<unsigned> indexes(data.size());
std::iota(indexes.begin(), indexes.end(), 0u);

要对数据进行排序,您需要使用自定义比较器函数对indexes进行排序,该函数会从data检索值并进行比较。请注意:indexes可以更改,但data不应该更改!

sort(indexes.begin(), indexes.end(), [&](unsigned a, unsigned b)
    {
        return data[a] < data[b];
    });

答案 1 :(得分:1)

只是一个想法:不要将std::string存储在向量中,只需将每个字符串的字符数组附加到std::vector<char>即可。

这将字符串紧密地打包在内存中,通过小字符串优化,比std::string更好地改善了局部性。如果字符串超过最大值,它也会产生更好的结果。小字符串优化的大小。

对于排序,在第二个向量中存储每个字符串的索引和大小,类似于Pubbys建议。

当然,只有在字符串长度不需要动态更改时才有效。否则你将不得不重建vector<char>

#include <iostream>
#include <algorithm>
#include <vector>
#include <utility>
#include <string_view>

using namespace std;

using IndexAndSize = pair<size_t,size_t>;

void push_and_index( vector<char>& v, vector<IndexAndSize>& vi, string_view s )
{
    vi.emplace_back( v.size(), s.size() );
    v.insert( end(v), begin(s), end(s) );
}

string_view make_string_view( vector<char> const& v, IndexAndSize is )
{
    return { v.data() + is.first, is.second };
}

int main()
{
    vector<char> v;
    vector<IndexAndSize> vi;

    push_and_index( v, vi, "foo" );
    push_and_index( v, vi, "bar" );
    push_and_index( v, vi, "foobar" );
    push_and_index( v, vi, "barfoo" );

    sort( begin(vi), end(vi), [&]( IndexAndSize a, IndexAndSize b )
    {
        return make_string_view( v, a ) < make_string_view( v, b );
    });

    for( IndexAndSize is : vi )
    {
        cout << make_string_view( v, is ) << endl;
    }
}

Live demo on Coliru.

注意:C ++ 17的string_view仅用于帮助排序和输出,对于这个想法并不重要。