Which is faster? std::priority_queue or std::multimap

Started by
17 comments, last by Cornstalks 11 years, 10 months ago
It's not just the insert which matters, it is the whole scope of how you plan to use this data structure as different structures have different charactristics depending on what you are going to do.

If you are looking up lists of data based on a common key then multimap might give you the best performance as it is designed for fast lookups.

On the other hand if you just want a list of data you can iterate over from first to last then something which allocates contiguous memory as a backing store will be faster (think std::vector)

It all comes down to usage pattern, without knowing that you can't make an informed choice as to what container you should use.
Advertisement

I honestly don't know what you are needing or looking for info on this, it's just a simple insert()...


pickedObjects.insert(double, Obj*); //Obj*just an address not new'd
//later on
pickedObjects.begin()->first to check the dist
pickedObjects.being()->second to assign that pointer to selected object....



Not sure what else I can say nothing complex here people....


I might be misinterpreting what you are talking about here, but if you are doing this for object picking, like having the user click in a window to select an object, and you only ever need to know the single object with the smallest distance, you don't need a priority_queue or a multimap. You just need to remember the currently closest object and its distance, and update those values if you find an even closer one.

Again, not sure if that is your use case, though.

mutlimap and priority_queue are totally different... one uses a mapped key and value, and the other uses a single value. Pick the one that fits most naturally with your problem. Don't abuse your toolbox.

That's not really all that fundamentally different. The difference between sorting by key and sorting by value is simply that in the latter case, your key is a part of your value. The former is a more general case of the latter.

Hence why you see things like QSet (value only) being implemented using a QHash (key value pair). The source is literally:

template <class T>
class QSet
{
typedef QHash<T, QHashDummyValue> Hash;
///...
private:
Hash q_hash;
};


QHash then relies on some specializations in choosing the node type to save memory, but even if it didn't the worst that could happen is that you'd use a little more memory to store each object. If that saved the developer the time of building and maintaining a completely new data structure with the desired performance characteristics, and if you had memory to spare, then nothing of value was lost.

There are other differences between multimap and priority_queue, but the fact that one is key/value and the other is value-only is not enough to say that one would be a natural fit and that the other would not. I think the expected read and write patterns are far more important.
Well so far from what I can determine, the std::multimap is faster when I start adding lots of objects. If one a few about the same.

Say player clicks on object and camera is at an angle where their could be 10 objects in a line one behind each other when doing the ray test, this shouldn't happen very often but seeing what I see so far, I am switching to the multimap version.

Say player clicks on object and camera is at an angle where their could be 10 objects in a line one behind each other when doing the ray test, this shouldn't happen very often but seeing what I see so far, I am switching to the multimap version.

You mean something like:

bool compare(const std::pair<double, Obj*>& left, const std::pair<double, Obj*>& right)
{
return left.first < right.first;
}

// Assuming you have an unsorted container of std::pair<double, Obj*> objects (like a std::vector)
std::vector<std::pair<double, Obj*> >::iterator min = std::min_element(myList.begin(), myList.end(), compare);


This code is O(n), even if you completely clear your vector every update. Using multimap or priority_queue is O(nlogn), assuming you clear them every update. However, taking SHilbert's advice into account, you can do the min tracking yourself (which is still O(n), but should require one or two less iterations over the container and book keeping required by the container, and thus potentially slightly more efficient than the above code).


*snip*

Yes, there are major differences between multimap and priority_queue in addition to the key/value thing. However, in that post I wasn't pointing out how different they are to implement, I was pointing out how different they are to use. Idiomatic uses of key/value maps or just value containers are quite different. And yes, you could abuse your toolbox and use them in a fairly similar manner (though not totally the same) with a couple unwieldy tricks, but I specifically advised against that. Proper use of these containers is quite different, because you use them in quite different situations.
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

struct GreaterSecond : std::binary_function<T, T, bool>
{
inline bool operator()(const T& lhs, const T& rhs)
{
return lhs.second > rhs.second;
}
};

typedef std::pair<NX::Object*, double> data_t;
typedef std::priority_queue<data_t, std::deque<data_t>, NX::Utility::GreaterSecond<data_t>> queue_t;
queue_t q(pickObjects.begin(), pickObjects.end());



Is what I was using when I did the priority_queue....

Well so far from what I can determine, the std::multimap is faster when I start adding lots of objects. If one a few about the same.

Say player clicks on object and camera is at an angle where their could be 10 objects in a line one behind each other when doing the ray test, this shouldn't happen very often but seeing what I see so far, I am switching to the multimap version.

If your worst case is literally finding the closest of a list of 10 objects once per frame, you could do anything and it would be fast. :)

On the other hand, if you can rule out raycasts before even doing them, that is definitely worth doing. Like, if you know that your closest object had a distance of 6 meters, and you get to an object where the distance from the camera to the surface of its bounding sphere is 7 meters, you don't have to do any ray casts vs. its geometry at all, you can just throw it out.
Thanks Cornstalks that worked great! I am now using the vector version you suggested.


struct GreaterSecond : std::binary_function<T, T, bool>
{
inline bool operator()(const T& lhs, const T& rhs)
{
return lhs.second > rhs.second;
}
};

typedef std::pair<NX::Object*, double> data_t;
typedef std::priority_queue<data_t, std::deque<data_t>, NX::Utility::GreaterSecond<data_t>> queue_t;
queue_t q(pickObjects.begin(), pickObjects.end());



Is what I was using when I did the priority_queue....

Which isn't just a simple insert... (because it's not an insert at all)

Really though, it would be very helpful if you were very clear on exactly what it is you're trying to do, and how you've tried to do it so far. You can't expect anyone to give you any meaningful help if you ask a question without much detail, then say it's X (just a simple insert), and then show us you've been doing Y (that's not an insert).

Optimizations aren't general. They're specific.* The fact that you're using the constructor to insert a range of things and not inserting them one by one makes a huge difference (by the way, the code you posted is O(n), with at most 3n comparisons being done, which isn't too bad). It shows that you a) already have the objects in an accessible, iteratable (is that a word?) data structure, b) either you only care about the top element, c) or maybe you just care about iterating through them in specific order. Your optimal solution depends on how many objects you're picking, how many objects you're picking from, what data structure these objects are stored in, how their distance is calculated, whether or not results can be reused, and even more details. None of which you've answered. Details are vital here, and so far all of your posts have been <= 3 sentences long.

If you're only picking one object, [font=courier new,courier,monospace]std::min_element[/font] will be up to ~3 times faster than your priority queue. If you're only picking from a few objects, you're probably optimizing the wrong thing.

[size=1]*generally speaking

[edit]

Glad to hear it worked well for you smile.png
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

This topic is closed to new replies.

Advertisement