矢量表现痛苦

时间:2014-12-08 19:17:53

标签: c++ performance vector

我一直致力于状态空间探索,并且最初使用地图来存储世界状态的分配,如map<Variable *, int>,其中变量是世界上的对象,域名从0到n,其中n是有限。实现的性能非常快,但我注意到它不能很好地扩展状态空间的大小。我改变状态使用vector<int>代替,在那里我使用变量的id在向量中找到它的索引。内存使用率大大提高,但求解器的效率已经下降(从<30秒到400+)。我修改的唯一代码是生成状态并验证状态是否为目标。我无法弄清楚为什么使用向量会降低性能,特别是因为向量运算在最坏的情况下只需要线性时间。

最初这是我生成节点的方式:

State * SuccessorGen::generate_successor(const Operator &op, map<Variable *, int> &var_assignment){

    map<Variable *, int> values;
    values.insert(var_assignment.begin(), var_assignment.end());
    vector<Operator::Effect> effect = op.get_effect();
    vector<Operator::Effect>::const_iterator eff_it = effect.begin();

    for (; eff_it != effect.end(); eff_it++){
        values[eff_it->var] = eff_it->after;
    }
    return new State(values);
}

在我的新实施中:

State* SuccessorGen::generate_successor(const Operator &op, const vector<int> &assignment){

    vector<int> child;
    child = assignment;

    vector<Operator::Effect> effect = op.get_effect();
    vector<Operator::Effect>::const_iterator eff_it = effect.begin();

    for (; eff_it != effect.end(); eff_it++){
        Variable *v = eff_it->var;
        int id = v->get_id();
        child[id] = eff_it->after;
    }
    return new State(child);
}

(目标检查类似,只是循环目标分配而不是操作员效果。)

这些矢量操作真的比使用地图慢吗?是否有一个同样高效的STL容器我可以使用,具有较低的开销?变量的数量相对较小(<50)并且在for循环之后永远不需要调整大小或修改向量。

编辑:

我尝试通过所有操作符定时循环以查看时序比较,效果列表和赋值矢量版本在0.3秒内运行一个循环,而地图版本稍微超过0.4秒。当我评论该地图的部分大致相同时,向量跳跃到接近0.5秒。我添加了child.reserve(assignment.size()),但这没有任何改变。

编辑2:

从user63710的回答中,我一直在挖掘其余的代码,并注意到在启发式计算中发生了一些非常奇怪的事情。矢量版本工作正常,但对于地图我使用这一行Node *n = new Node(i, transition.value, label_cost); open_list.push(n);,但一旦循环完成填充队列,节点就完全搞砸了。节点是一个简单的结构:

struct Node{
    // Source Value, Destination Value
    int from;
    int to;
    int distance;
    Node(int &f, int &t, int &d) : from(f), to(t), distance(d){}
};

不是from, to, distance,而是用from替换toid一些随机数,并且搜索不会做到应该做的事情并且返回得更快那应该。当我调整地图版本以将地图转换为矢量并运行时:

Node n(i, transition.value, label_cost); open_list.push(n); 性能大约等于向量的性能。这样就解决了我的主要问题,但是这让我想知道为什么使用Node *n会导致这种行为与Node n()相反?

2 个答案:

答案 0 :(得分:0)

问题在于您在不预留空间的情况下创建向量。向量连续存储元素。这确保了对元素的持续访问。

因此,每次向向量添加项目时(例如通过插入器),向量都必须重新分配更多空间,并最终将所有现有元素移动到重新分配的内存位置。这会导致速度减慢和相当大的堆碎片。

如果您事先知道您将拥有多少元素,那么解决方案就是reserve()元素。或者,如果您不保留()较大的块并比较size()capacity()以检查是否有时间保留更多。

答案 1 :(得分:0)

如果如你所说,这些结构的大小相当小(约50个元素),我不得不认为问题出在其他地方。至少,我认为它不涉及内存访问或向量/映射的分配。

我测试的一些示例代码:地图版本:

unique_ptr<map<int, int>> make_successor_map(const vector<int> &ids,
    const map<int, int> &input)
{
    auto new_map = make_unique<map<int, int>>(input.begin(), input.end());

    for (size_t i = 0; i < ids.size(); ++i)
        swap((*new_map)[ids[i]], (*new_map)[i]);

    return new_map;
}

int main()
{
    auto a_map = make_unique<map<int, int>>();

    // ids to access
    vector<int> ids;

    const int n = 100;
    for (int i = 0; i < n; ++i)
    {
        a_map->insert({i, rand()});
        ids.push_back(i);
    }

    random_shuffle(ids.begin(), ids.end());

    for (int i = 0; i < 1e6; ++i)
    {
        auto temp_map = make_successor_map(ids, *a_map);
        swap(temp_map, a_map);
    }

    cout << a_map->begin()->second << endl;
}

矢量版:

unique_ptr<vector<int>> make_successor_vec(const vector<int> &ids,
    const vector<int> &input)
{
    auto new_vec = make_unique<vector<int>>(input);

    for (size_t i = 0; i < ids.size(); ++i)
        swap((*new_vec)[ids[i]], (*new_vec)[i]);

    return new_vec;
}

int main()
{
    auto a_vec = make_unique<vector<int>>();

    // ids to access
    vector<int> ids;

    const int n = 100;
    for (int i = 0; i < n; ++i)
    {
        a_vec->push_back(rand());
        ids.push_back(i);
    }

    random_shuffle(ids.begin(), ids.end());

    for (int i = 0; i < 1e6; ++i)
    {
        auto temp_vec = make_successor_vec(ids, *a_vec);
        swap(temp_vec, a_vec);
    }

    cout << *a_vec->begin() << endl;
}

地图版本需要大约15秒才能在我的旧Core 2 Duo T9600上运行,矢量版需要0.406秒。我们都使用g++ -O3 --std=c++1y在G ++ 4.9.2上编译。因此,如果你的代码每次迭代需要0.4秒(注意我的示例代码需要0.4s才能进行100万次调用),那么我真的认为你的问题在其他地方。

这并不是说你没有因为从map-&gt;向量切换而导致性能下降,而是你发布的代码没有太多理由发生这种情况。