I have some C++ code where I need to implement cache replacement using LRU technique.
So far I know two methods to implement LRU cache replacement:
Using timeStamp for each time the cached data is accessed and finally comparing the timeStamps at time of replacement.
Using a stack of cached items and moving them to the top if they are accessed recently, so finally the bottom will contain the LRU Candidate.
So, which of these is better to be used in production code?
Are their any other better methods?
Recently I implemented a LRU cache using a linked list spread over a hash map.
/// Typedef for URL/Entry pair
typedef std::pair< std::string, Entry > EntryPair;
/// Typedef for Cache list
typedef std::list< EntryPair > CacheList;
/// Typedef for URL-indexed map into the CacheList
typedef boost::unordered_map< std::string, CacheList::iterator > CacheMap;
/// Cache LRU list
CacheList mCacheList;
/// Cache map into the list
CacheMap mCacheMap;
It has the advantage of being O(1) for all important operations.
The insertion algorithm:
// create new entry
Entry iEntry( ... );
// push it to the front;
mCacheList.push_front( std::make_pair( aURL, iEntry ) );
// add it to the cache map
mCacheMap[ aURL ] = mCacheList.begin();
// increase count of entries
mEntries++;
// check if it's time to remove the last element
if ( mEntries > mMaxEntries )
{
// erease from the map the last cache list element
mCacheMap.erase( mCacheList.back().first );
// erase it from the list
mCacheList.pop_back();
// decrease count
mEntries--;
}
Here is a very simple implementation of LRU cache
https://github.com/lamerman/cpp-lru-cache .
It's easy to use and understand how it works. The total size of code is about 50 lines.
For simplicity, maybe you should consider using Boost's MultiIndex map. If we separate the key from the data, we support multiple sets of keys on the same data.
From [ http://old.nabble.com/realization-of-Last-Recently-Used-cache-with-boost%3A%3Amulti_index-td22326432.html ]:
"...use two indexes: 1) hashed for searching value by key 2) sequential for tracking last recently used items (get function put item as last item in sequesnce. If we need to remove some items from cache, we may delete they from begin of sequence)."
Note that the "project" operator "allows the programmer to move between different indices of the same multi_index_container" efficiently.
This article describes implementation using a pair of STL containers (a key-value map plus a list for the key access history), or a single boost::bimap.
In our production environment we use a C++ double linked list which is similar to the Linux kernel linked list. The beauty of it is that you can add an object to as many linked lists as you want and list operation is fast and simple.
This can be done with boost/compute/detail/lru_cache.hpp. Here is a basic example using it.
#include <boost/compute/detail/lru_cache.hpp>
...
// create an instance that maps from a double to a string and has a max size of 1000
auto my_lru_cache = boost::compute::detail::lru_cache<double, std::string>(1000);
my_lru_cache.insert(3.14, "pi");
if (my_lru_cache.contains(3.14))
{
// the first get returns a boost::optional
auto value = my_lru_cache.get(3.14).get();
std::cout << value << "\n";
}
Related
I'm trying to come up with a way to define a flow graph (think TBB) defined at runtime. Currently, we use TBB to define the nodes and the edges between the nodes at compile time. This is sort of annoying because we have people who want to add processing steps and modify the processing chain without recompiling the whole application or really having to know anything about the application beyond how to add processing kernels. In an ideal world I would have some sort of plugin framework using dlls. We already have the software architected so that each node in TBB represents a processing step so it's pretty easy to add stuff if you're willing to recompile.
As a first step, I was trying to come up with a way to define a TBB flow graph in YAML but it was a massive rabbit hole. Does anyone know if something like this exists before I go all in on implementing this from scratch? It will be a fun project but no point in duplicating work.
I am not sure if anything like this exists in a TBB companion library but it is definitely doable to implement a small subset of the functionalities of Flow Graph configurable at runtime.
If the data that transit through your graph have a well defined type, aka your nodes are basically function_node<T, T> things are manageable. If the Graph transforms data from one type to another it gets more complicated -one solution would be to use a variant of these types and handle the possibly incompatible types at runtime. That really depends on the level of flexibility required.
With:
$ cat nodes.txt
# type concurrency params...
multiply unlimited 2
affine serial 5 -3
and
$ cat edges.txt
# src dst
0 1
1 2
where index 0 is a source node, here is a scaffold of how I would implement it:
using data_t = double;
using node_t = function_node<data_t , data_t >;
graph g;
std::vector<node_t> nodes;
auto node_factory = [&g](std::string type, std::string concurrency, std::string params) -> node_t {
// Implement a dynamic factory of nodes
};
// Add source node first
nodes.push_back(flow::input_node<data_t>(g,
[&]( oneapi::tbb::flow_control &fc ) -> data_t { /*...*/ });
// Parse the node description file and populate the node vector using the factory
for (auto&& n : nodes)
nodes.push_back(node_factory(n.type, n.concurrency, n.params));
// Parse the edge description file and call make_edge accordingly
for (auto&& e : edges)
flow::make_edge(nodes[e.src], nodes[e.dst]);
// Run the graph
nodes[0].activate();
g.wait_for_all();
QHash<QPair<QString N_id, QString A_id>, QString name> info
I have this QHash , and i have the values of N_id and name for a particular index, how can i obtain the value of corresponding A_id. I am trying to use STL-style iterator. I can change QHash to QMap if needed but I cannot use:
QHash<QPair<QString N_id, QString name>, QString A_id>
Edit: N_id and A_id together forms a unique key in my case.
I think the major problem here is that QHash, being a hash table, looks up the values by hashing the keys. Hence, it needs to complete key to be able to look up a value; a "partial" key won't suffice - there's going to be no concrete object to hash then. A similar problem arises with a map: to navigate the BST, you need the complete object in order to make comparisons and left / right decisions. Thus, short of going back to the drawing board and revising your approach, I'd say, maintain a backwards map, be it a QHash or a QMap, with the mapping name -> pair(n_id, a_id). The downside is that you're going to have to keep the two in sync.
However, with the existing data structure, I'd perform a query like this:
#include <algorithm>
QHash<QPair<QString, QString>, QString> info;
QString a_n_id {/*...*/}; // the target N_id
QString a_name {/*...*/}; // the target name
/* ... */
const auto keyList = info.keys(a_name); // QList<QPair<QString, QString> >
std::find_if(keyList.begin(), keyList.end(),
[&](decltype(info)::key_type& key) { return key.first == a_n_id; });
See this question in case decltype(info)::value_type refuses to build on Microsoft VS.
This is of course going to be linear, since, as I've already said, a hash needs the complete object to be able to perform a lookup, hence we can't use the logarithmic complexity lookup in this case.
I am trying to get my head around making this requirement as efficient as possible, because it is part of a combinatorial problem solver, so every little bit helps in the grand scheme of things.
Lets say I have a list of elements, in this case called transitions.
val possibleTransitions : List[Transition] = List[...] //coming from somewhere
I want to perform an (somewhat expensive) computation on each transition, to obtain another object, in this case called a State.
The natural way for me to do it is using a for-comprehension or a map. The former for me is more convenient because I want to filter out a few irrelevant State objects, such as those which were already processed earlier.
val newStates = for {
transition <- possibleTransitions
state <- computeExpensiveOperation(transition)
if (confirmNewState(state))
} yield state
State contains a value, lets call it value(), which indicates some kind of attractiveness of that state. If the value() is very low (attractive) I want to discard the rest of the list and use that. Since possibleTransitions could be a very long list (thousands), ideally I avoid doing that computeExpensiveOperation if for example the first State object already has the value() I want.
On the other hand, if I don't find any item with an attractive value() I want to keep all of them and add them to another list.
val newPending = pending ++ newStates
I was trying to use a Stream for this, to avoid computing all the values before processing them. If I use find() and I don't find the required item then I won't be able to get the items in the stream (since its use-once).
The only thing I can see possible at the moment is to use possibleItems.toStream() in the for-comprehension and create another collection, iterating through each item one by one until either I find the item (and discard the collection) or no (and use the collection with all items).
Am I missing some smarter more efficient way to do this?
I would use lazy views and convert them to a stream to cache the intermediate result, then you can get the information you need:
val newStates = for {
transition <- possibleTransitions.view
state <- computeExpensiveOperation(transition)
if (confirmNewState(state))
} yield state
val newStatesStream = newStates.toStream // cache results
val attractive = newStatesStream.find(isAttractive(_))
attractive match {
case Some(a) => // do whatever
case None => {
val newPending = pending ++ newStatesSteam
}
}
As the stream is lazy it will only be computed until the first element is found in the line with val attractive. If there is no attractive element the complete stream will be computed and cached and None will be returned.
When computing the new pending elements we can just append this stream to pending. (By the way: pending should probably be a Queue)
I want to use AdaBoost to choose a good set features from a large number (~100k). AdaBoost works by iterating though the feature set and adding in features based on how well they preform. It chooses features that preform well on samples that were mis-classified by the existing feature set.
Im currently using in Open CV's CvBoost. I got an example working, but from the documentation it is not clear how to pull out the feature indexes that It has used.
Using either CvBoost, a 3rd party library or implementing it myself, how can pull out a set of features from a large feature set using AdaBoot?
With the help of #greeness answer I made a subclass of CvBoost
std::vector<int> RSCvBoost::getFeatureIndexes() {
CvSeqReader reader;
cvStartReadSeq( weak, &reader );
cvSetSeqReaderPos( &reader, 0 );
std::vector<int> featureIndexes;
int weak_count = weak->total;
for( int i = 0; i < weak_count; i++ ) {
CvBoostTree* wtree;
CV_READ_SEQ_ELEM( wtree, reader );
const CvDTreeNode* node = wtree->get_root();
CvDTreeSplit* split = node->split;
const int index = split->condensed_idx;
// Only add features that are not already added
if (std::find(featureIndexes.begin(),
featureIndexes.end(),
index) == featureIndexes.end()) {
featureIndexes.push_back(index);
}
}
return featureIndexes;
}
Claim: I am not a user of opencv. From the documentation, opencv's adaboost is using the decision tree (either classification tree or regression tree) as the fundamental weak learner.
It seems to me this is the way to get the underline weak learners:
CvBoost::get_weak_predictors
Returns the sequence of weak tree classifiers.
C++: CvSeq* CvBoost::get_weak_predictors()
The method returns the sequence of weak classifiers.
Each element of the sequence is a pointer to the CvBoostTree class or
to some of its derivatives.
Once you have access to the sequence of CvBoostTree*, you should be able to inspect which features are contained in the tree and what are the split value etc.
If each tree is only a decision stump, only one feature is contained in each weak learner. But if we allow deeper depth of tree, a combination of features could exist in each individual weak learner.
I further took a look at the CvBoostTree class; unfortunately the class itself does not provide a public method to check the internal features used. But you might want to create your own sub-class inheriting from CvBoostTree and expose whatever functionality.
I'm writing something like a game in C++ where I have a database table containing the current score for each user. I want to read that table into memory at the start of the game, quickly change each user's score while the game is being played in response to what each user does, and then when the game ends write the current scores back to the database. I also want to be able to find the 20 or so users with the highest scores. No users will be added or deleted during the short period when the game is being played. I haven't tried it yet, but updating the database might take too much time during the period when the game is being played.
Fixed set of users (might be 10,000 to 50,000 users)
Will map user IDs to their score and other user-specific information.
User IDs will be auto_increment values.
If the structure has a high memory overhead that's probably not an issue.
If the program crashes during gameplay it can just be re-started.
Greatly prefer something already available, such as open source/public domain code.
Quickly get a user's current score.
Quickly add to a user's current score (and return their current score)
Quickly get 20 users with highest score.
No deletes.
No inserts except when the structure is first created, and how long that takes isn't critical.
Getting the top 20 users will only happen every five or ten seconds, but getting/adding will happen much more frequently.
If not for the last, I could just create a memory block equal to sizeof(user) * max(user id) and put each user at user id * sizeof(user) for fast access. Should I do that plus some other structure for the Top 20 feature, or is there one structure that will handle all of this together?
Use a std::map. In the incredibly unlikely event that it ever shows up in your profiling, you could maybe think about changing to something more exotic. Memory overhead for 50k users will be around a megabyte or two.
I doubt that iterating over a map with 50k entries every 5-10 seconds, to find the top scores, will introduce significant overhead. If it does, though, either use a Boost multi-index container, or maintain a separate structure for the hi-scores (a heap, or just an array of pointers to the current top 20, in order). Just with an array / vector of 20, the code to increment a score might look something like this (assuming scores only go up, not down):
player.score += points;
if (player.score > hiscores[19]->score) {
hiscore_dirty = true;
}
And the code to get the hi-scores:
if (hiscore_dirty) {
recalculate_hiscores();
hiscore_dirty = false;
}
std::for_each(hiscores.begin(), hiscores.end(), do_something);
If your "auto-increment" and "no delete" policies are fixed forever (i.e. you will never delete users from the DB), and therefore user ids truly are a contiguous range from 0 to the limit, then you should just use a std::vector instead of a std::map.
You might be interested in Fibonacci Heap. This has O(1) (amortized) increaseKey and findMax.
For more info on Heap in general refer: Heap Data Structure, especially the table which compares different heaps.
An implementation of Fibonacci Heap can be found here which you can perhaps use/get inspired from: http://resnet.uoregon.edu/~gurney_j/jmpc/fib.html
First of all, given that you have a Key/Value scenario, you should probably use an Associative Container.
If you are using plain old C++ and do not have Boost available, follow Steve Jessops's suggestion and simply use a std::map, if you have either C++0x or Boost, you'd better use a hash_map or unordered_map: it just matches your requirements better (you don't need to order the players by id after all, you just want to find them quickly) and will probably be faster given the number of players.
For managing the top20 you have 2 choices:
You could use the Boost.MultiIndex library to create one unique container that both offers fast lookup on ID (using a hash map) and an ordered index on the score... however it's a bit of a waste to order all players when you only need 20 of them
You can simply manages a separate structure, like a vector of pointers to users, and each time you modify the score of a user check it should replace a user in the vector
The last solution, though simple, assumes that a player cannot lose points... it's much more difficult if that may happen.
class UsersCollection;
class User
{
public:
void incrementScore(size_t term);
private:
size_t mId;
size_t mScore;
UsersCollection& mCollection;
};
class UsersCollection
{
public:
static const size_t MNumberHiScores = 20;
static const size_t MNotAChampion = -1;
UsersCollection(DBConnection const&);
// returns either the position of the user in
// the hi scores vector or MNotAChampion
size_t insertUserInHiScores(User const& user);
private:
std::unordered_map<size_t, User> mUsers;
std::vector<User const*> mHiScores; // [1]
};
void User::incrementScore(size_t term)
{
mScore += term;
mCollection.insertUserInHiScores(*this);
}
struct UserSort: std::binary_function<User const*, User const*, bool>
{
bool operator()(User const* lhs, User const* rhs) const
{
return lhs->score() > rhs->score();
}
};
size_t UsersCollection::insertUserInHiScores(User const& user)
{
std::vector<User const*>::const_iterator it =
std::find(mHiScores.begin(), mHiScores.end(), &user);
if (it == mHiScores.end()) // not among the hiscores
{
mHiScores.push_back(&user);
}
std::sort(mHiScores.begin(), mHiScores.end(), UserSort());
if (mHiScores.size() > MNumberHiScores) // purge if too many users
{
User const* last = mHiScores.back();
mHiScores.pop_back();
if (&user == last) return MNotAChampion;
}
// return position in the vector in the [0, MNumberHiScores) range
return std::find(mHiScores.begin(), mHiScores.end(), &user)
- mHiScores.begin();
}
Note (1): using a set may seem a good idea however a set presumes that the elements do not change and it is not the case. It could work if we were very careful:
remove the user from the set before changing the score
putting the user back in once it has changed
optionally popping the last elements if there are too many of them