• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • Examples
  • File List

node_cache.h

00001 /***************************************************************************
00002  *  include/stxxl/bits/containers/btree/node_cache.h
00003  *
00004  *  Part of the STXXL. See http://stxxl.sourceforge.net
00005  *
00006  *  Copyright (C) 2006 Roman Dementiev <[email protected]>
00007  *
00008  *  Distributed under the Boost Software License, Version 1.0.
00009  *  (See accompanying file LICENSE_1_0.txt or copy at
00010  *  http://www.boost.org/LICENSE_1_0.txt)
00011  **************************************************************************/
00012 
00013 #ifndef STXXL_CONTAINERS_BTREE__NODE_CACHE_H
00014 #define STXXL_CONTAINERS_BTREE__NODE_CACHE_H
00015 
00016 #ifdef STXXL_BOOST_CONFIG
00017  #include <boost/config.hpp>
00018 #endif
00019 
00020 #include <stxxl/bits/compat_hash_map.h>
00021 
00022 #include <stxxl/bits/io/request.h>
00023 #include <stxxl/bits/mng/mng.h>
00024 #include <stxxl/bits/mng/typed_block.h>
00025 
00026 #include <stxxl/bits/containers/btree/btree_pager.h>
00027 
00028 
00029 __STXXL_BEGIN_NAMESPACE
00030 
00031 // TODO:  speedup BID2node_ access using search result iterator in the methods
00032 
00033 namespace btree
00034 {
00035     template <class NodeType, class BTreeType>
00036     class node_cache : private noncopyable
00037     {
00038     public:
00039         typedef BTreeType btree_type;
00040         typedef NodeType node_type;
00041         typedef typename node_type::block_type block_type;
00042         typedef typename node_type::bid_type bid_type;
00043         typedef typename btree_type::key_compare key_compare;
00044 
00045         typedef typename btree_type::alloc_strategy_type alloc_strategy_type;
00046         typedef stxxl::btree::lru_pager pager_type;
00047 
00048     private:
00049         btree_type * btree_;
00050         key_compare comp_;
00051 
00052 /*
00053         struct bid_comp
00054         {
00055             bool operator ()  (const bid_type & a, const bid_type & b) const
00056             {
00057                 return (a.storage < b.storage) || ( a.storage == b.storage && a.offset < b.offset);
00058             }
00059         };
00060 */
00061 
00062         struct bid_hash
00063         {
00064             size_t operator () (const bid_type & bid) const
00065             {
00066                 size_t result =
00067                     longhash1(bid.offset + uint64(unsigned_type(bid.storage)));
00068                 return result;
00069             }
00070 #ifdef BOOST_MSVC
00071             bool operator () (const bid_type & a, const bid_type & b) const
00072             {
00073                 return (a.storage < b.storage) || (a.storage == b.storage && a.offset < b.offset);
00074             }
00075             enum
00076             {                                   // parameters for hash table
00077                 bucket_size = 4,                // 0 < bucket_size
00078                 min_buckets = 8                 // min_buckets = 2 ^^ N, 0 < N
00079             };
00080 #endif
00081         };
00082 
00083         std::vector<node_type *> nodes_;
00084         std::vector<request_ptr> reqs_;
00085         std::vector<bool> fixed_;
00086         std::vector<bool> dirty_;
00087         std::vector<int_type> free_nodes_;
00088         typedef typename compat_hash_map<bid_type, int_type, bid_hash>::result hash_map_type;
00089 
00090         //typedef std::map<bid_type,int_type,bid_comp> BID2node_type;
00091         typedef hash_map_type BID2node_type;
00092 
00093         BID2node_type BID2node_;
00094         stxxl::btree::lru_pager pager_;
00095         block_manager * bm;
00096         alloc_strategy_type alloc_strategy_;
00097 
00098         int64 n_found;
00099         int64 n_not_found;
00100         int64 n_created;
00101         int64 n_deleted;
00102         int64 n_read;
00103         int64 n_written;
00104         int64 n_clean_forced;
00105 
00106         // changes btree pointer in all contained iterators
00107         void change_btree_pointers(btree_type * b)
00108         {
00109             typename std::vector<node_type *>::const_iterator it = nodes_.begin();
00110             for ( ; it != nodes_.end(); ++it)
00111             {
00112                 (*it)->btree_ = b;
00113             }
00114         }
00115 
00116     public:
00117         node_cache(unsigned_type cache_size_in_bytes,
00118                    btree_type * btree__,
00119                    key_compare comp__
00120                    ) :
00121             btree_(btree__),
00122             comp_(comp__),
00123             bm(block_manager::get_instance()),
00124             n_found(0),
00125             n_not_found(0),
00126             n_created(0),
00127             n_deleted(0),
00128             n_read(0),
00129             n_written(0),
00130             n_clean_forced(0)
00131         {
00132             const unsigned_type nnodes = cache_size_in_bytes / block_type::raw_size;
00133             STXXL_VERBOSE1("btree::node_cache constructor nodes=" << nnodes);
00134             if (nnodes < 3)
00135             {
00136                 STXXL_THROW(std::runtime_error, "btree::node_cache::node_cache", "Too few memory for a node cache (<3)");
00137             }
00138             nodes_.reserve(nnodes);
00139             reqs_.resize(nnodes);
00140             free_nodes_.reserve(nnodes);
00141             fixed_.resize(nnodes, false);
00142             dirty_.resize(nnodes, true);
00143             for (unsigned_type i = 0; i < nnodes; ++i)
00144             {
00145                 nodes_.push_back(new node_type(btree_, comp_));
00146                 free_nodes_.push_back(i);
00147             }
00148 
00149             pager_type tmp_pager(nnodes);
00150             std::swap(pager_, tmp_pager);
00151         }
00152 
00153         unsigned_type size() const
00154         {
00155             return nodes_.size();
00156         }
00157 
00158         // returns the number of fixed pages
00159         unsigned_type nfixed() const
00160         {
00161             typename BID2node_type::const_iterator i = BID2node_.begin();
00162             typename BID2node_type::const_iterator end = BID2node_.end();
00163             unsigned_type cnt = 0;
00164             for ( ; i != end; ++i)
00165             {
00166                 if (fixed_[(*i).second])
00167                     ++cnt;
00168             }
00169             return cnt;
00170         }
00171 
00172         ~node_cache()
00173         {
00174             STXXL_VERBOSE1("btree::node_cache destructor addr=" << this);
00175             typename BID2node_type::const_iterator i = BID2node_.begin();
00176             typename BID2node_type::const_iterator end = BID2node_.end();
00177             for ( ; i != end; ++i)
00178             {
00179                 const unsigned_type p = (*i).second;
00180                 if (reqs_[p].valid())
00181                     reqs_[p]->wait();
00182 
00183                 if (dirty_[p])
00184                     nodes_[p]->save();
00185             }
00186             for (unsigned_type i = 0; i < size(); ++i)
00187                 delete nodes_[i];
00188         }
00189 
00190         node_type * get_new_node(bid_type & new_bid)
00191         {
00192             ++n_created;
00193 
00194             if (free_nodes_.empty())
00195             {
00196                 // need to kick a node
00197                 int_type node2kick;
00198                 unsigned_type i = 0;
00199                 const unsigned_type max_tries = size() + 1;
00200                 do
00201                 {
00202                     ++i;
00203                     node2kick = pager_.kick();
00204                     if (i == max_tries)
00205                     {
00206                         STXXL_ERRMSG(
00207                             "The node cache is too small, no node can be kicked out (all nodes are fixed) !");
00208                         STXXL_ERRMSG("Returning NULL node.");
00209                         return NULL;
00210                     }
00211                     pager_.hit(node2kick);
00212                 } while (fixed_[node2kick]);
00213 
00214 
00215                 if (reqs_[node2kick].valid())
00216                     reqs_[node2kick]->wait();
00217 
00218 
00219                 node_type & Node = *(nodes_[node2kick]);
00220 
00221                 if (dirty_[node2kick])
00222                 {
00223                     Node.save();
00224                     ++n_written;
00225                 }
00226                 else
00227                     ++n_clean_forced;
00228 
00229 
00230                 //reqs_[node2kick] = request_ptr(); // reset request
00231 
00232                 assert(BID2node_.find(Node.my_bid()) != BID2node_.end());
00233                 BID2node_.erase(Node.my_bid());
00234                 bm->new_block(alloc_strategy_, new_bid);
00235 
00236                 BID2node_[new_bid] = node2kick;
00237 
00238                 Node.init(new_bid);
00239 
00240                 dirty_[node2kick] = true;
00241 
00242                 assert(size() == BID2node_.size() + free_nodes_.size());
00243 
00244                 STXXL_VERBOSE1("btree::node_cache get_new_node, need to kick node " << node2kick);
00245 
00246                 return &Node;
00247             }
00248 
00249 
00250             int_type free_node = free_nodes_.back();
00251             free_nodes_.pop_back();
00252             assert(fixed_[free_node] == false);
00253 
00254             bm->new_block(alloc_strategy_, new_bid);
00255             BID2node_[new_bid] = free_node;
00256             node_type & Node = *(nodes_[free_node]);
00257             Node.init(new_bid);
00258 
00259             // assert(!(reqs_[free_node].valid()));
00260 
00261             pager_.hit(free_node);
00262 
00263             dirty_[free_node] = true;
00264 
00265             assert(size() == BID2node_.size() + free_nodes_.size());
00266 
00267             STXXL_VERBOSE1("btree::node_cache get_new_node, free node " << free_node << "available");
00268 
00269             return &Node;
00270         }
00271 
00272 
00273         node_type * get_node(const bid_type & bid, bool fix = false)
00274         {
00275             typename BID2node_type::const_iterator it = BID2node_.find(bid);
00276             ++n_read;
00277 
00278             if (it != BID2node_.end())
00279             {
00280                 // the node is in cache
00281                 const int_type nodeindex = it->second;
00282                 STXXL_VERBOSE1("btree::node_cache get_node, the node " << nodeindex << "is in cache , fix=" << fix);
00283                 fixed_[nodeindex] = fix;
00284                 pager_.hit(nodeindex);
00285                 dirty_[nodeindex] = true;
00286 
00287                 if (reqs_[nodeindex].valid() && !reqs_[nodeindex]->poll())
00288                     reqs_[nodeindex]->wait();
00289 
00290 
00291                 ++n_found;
00292                 return nodes_[nodeindex];
00293             }
00294 
00295             ++n_not_found;
00296 
00297             // the node is not in cache
00298             if (free_nodes_.empty())
00299             {
00300                 // need to kick a node
00301                 int_type node2kick;
00302                 unsigned_type i = 0;
00303                 const unsigned_type max_tries = size() + 1;
00304                 do
00305                 {
00306                     ++i;
00307                     node2kick = pager_.kick();
00308                     if (i == max_tries)
00309                     {
00310                         STXXL_ERRMSG(
00311                             "The node cache is too small, no node can be kicked out (all nodes are fixed) !");
00312                         STXXL_ERRMSG("Returning NULL node.");
00313                         return NULL;
00314                     }
00315                     pager_.hit(node2kick);
00316                 } while (fixed_[node2kick]);
00317 
00318                 if (reqs_[node2kick].valid())
00319                     reqs_[node2kick]->wait();
00320 
00321 
00322                 node_type & Node = *(nodes_[node2kick]);
00323 
00324                 if (dirty_[node2kick])
00325                 {
00326                     Node.save();
00327                     ++n_written;
00328                 }
00329                 else
00330                     ++n_clean_forced;
00331 
00332 
00333                 BID2node_.erase(Node.my_bid());
00334 
00335                 reqs_[node2kick] = Node.load(bid);
00336                 BID2node_[bid] = node2kick;
00337 
00338                 fixed_[node2kick] = fix;
00339 
00340                 dirty_[node2kick] = true;
00341 
00342                 assert(size() == BID2node_.size() + free_nodes_.size());
00343 
00344                 STXXL_VERBOSE1("btree::node_cache get_node, need to kick node" << node2kick << " fix=" << fix);
00345 
00346                 return &Node;
00347             }
00348 
00349             int_type free_node = free_nodes_.back();
00350             free_nodes_.pop_back();
00351             assert(fixed_[free_node] == false);
00352 
00353             node_type & Node = *(nodes_[free_node]);
00354             reqs_[free_node] = Node.load(bid);
00355             BID2node_[bid] = free_node;
00356 
00357             pager_.hit(free_node);
00358 
00359             fixed_[free_node] = fix;
00360 
00361             dirty_[free_node] = true;
00362 
00363             assert(size() == BID2node_.size() + free_nodes_.size());
00364 
00365             STXXL_VERBOSE1("btree::node_cache get_node, free node " << free_node << "available, fix=" << fix);
00366 
00367             return &Node;
00368         }
00369 
00370         node_type const * get_const_node(const bid_type & bid, bool fix = false)
00371         {
00372             typename BID2node_type::const_iterator it = BID2node_.find(bid);
00373             ++n_read;
00374 
00375             if (it != BID2node_.end())
00376             {
00377                 // the node is in cache
00378                 const int_type nodeindex = it->second;
00379                 STXXL_VERBOSE1("btree::node_cache get_node, the node " << nodeindex << "is in cache , fix=" << fix);
00380                 fixed_[nodeindex] = fix;
00381                 pager_.hit(nodeindex);
00382 
00383                 if (reqs_[nodeindex].valid() && !reqs_[nodeindex]->poll())
00384                     reqs_[nodeindex]->wait();
00385 
00386 
00387                 ++n_found;
00388                 return nodes_[nodeindex];
00389             }
00390 
00391             ++n_not_found;
00392 
00393             // the node is not in cache
00394             if (free_nodes_.empty())
00395             {
00396                 // need to kick a node
00397                 int_type node2kick;
00398                 unsigned_type i = 0;
00399                 const unsigned_type max_tries = size() + 1;
00400                 do
00401                 {
00402                     ++i;
00403                     node2kick = pager_.kick();
00404                     if (i == max_tries)
00405                     {
00406                         STXXL_ERRMSG(
00407                             "The node cache is too small, no node can be kicked out (all nodes are fixed) !");
00408                         STXXL_ERRMSG("Returning NULL node.");
00409                         return NULL;
00410                     }
00411                     pager_.hit(node2kick);
00412                 } while (fixed_[node2kick]);
00413 
00414                 if (reqs_[node2kick].valid())
00415                     reqs_[node2kick]->wait();
00416 
00417 
00418                 node_type & Node = *(nodes_[node2kick]);
00419                 if (dirty_[node2kick])
00420                 {
00421                     Node.save();
00422                     ++n_written;
00423                 }
00424                 else
00425                     ++n_clean_forced;
00426 
00427 
00428                 BID2node_.erase(Node.my_bid());
00429 
00430                 reqs_[node2kick] = Node.load(bid);
00431                 BID2node_[bid] = node2kick;
00432 
00433                 fixed_[node2kick] = fix;
00434 
00435                 dirty_[node2kick] = false;
00436 
00437                 assert(size() == BID2node_.size() + free_nodes_.size());
00438 
00439                 STXXL_VERBOSE1("btree::node_cache get_node, need to kick node" << node2kick << " fix=" << fix);
00440 
00441                 return &Node;
00442             }
00443 
00444             int_type free_node = free_nodes_.back();
00445             free_nodes_.pop_back();
00446             assert(fixed_[free_node] == false);
00447 
00448             node_type & Node = *(nodes_[free_node]);
00449             reqs_[free_node] = Node.load(bid);
00450             BID2node_[bid] = free_node;
00451 
00452             pager_.hit(free_node);
00453 
00454             fixed_[free_node] = fix;
00455 
00456             dirty_[free_node] = false;
00457 
00458             assert(size() == BID2node_.size() + free_nodes_.size());
00459 
00460             STXXL_VERBOSE1("btree::node_cache get_node, free node " << free_node << "available, fix=" << fix);
00461 
00462             return &Node;
00463         }
00464 
00465         void delete_node(const bid_type & bid)
00466         {
00467             typename BID2node_type::const_iterator it = BID2node_.find(bid);
00468             try
00469             {
00470                 if (it != BID2node_.end())
00471                 {
00472                     // the node is in the cache
00473                     const int_type nodeindex = it->second;
00474                     STXXL_VERBOSE1("btree::node_cache delete_node " << nodeindex << " from cache.");
00475                     if (reqs_[nodeindex].valid())
00476                         reqs_[nodeindex]->wait();
00477 
00478                     //reqs_[nodeindex] = request_ptr(); // reset request
00479                     free_nodes_.push_back(nodeindex);
00480                     BID2node_.erase(bid);
00481                     fixed_[nodeindex] = false;
00482                 }
00483                 ++n_deleted;
00484             } catch (const io_error & ex)
00485             {
00486                 bm->delete_block(bid);
00487                 throw io_error(ex.what());
00488             }
00489             bm->delete_block(bid);
00490         }
00491 
00492 
00493         void prefetch_node(const bid_type & bid)
00494         {
00495             if (BID2node_.find(bid) != BID2node_.end())
00496                 return;
00497 
00498 
00499             // the node is not in cache
00500             if (free_nodes_.empty())
00501             {
00502                 // need to kick a node
00503                 int_type node2kick;
00504                 unsigned_type i = 0;
00505                 const unsigned_type max_tries = size() + 1;
00506                 do
00507                 {
00508                     ++i;
00509                     node2kick = pager_.kick();
00510                     if (i == max_tries)
00511                     {
00512                         STXXL_ERRMSG(
00513                             "The node cache is too small, no node can be kicked out (all nodes are fixed) !");
00514                         STXXL_ERRMSG("Returning NULL node.");
00515                         return;
00516                     }
00517                     pager_.hit(node2kick);
00518                 } while (fixed_[node2kick]);
00519 
00520                 if (reqs_[node2kick].valid())
00521                     reqs_[node2kick]->wait();
00522 
00523 
00524                 node_type & Node = *(nodes_[node2kick]);
00525 
00526                 if (dirty_[node2kick])
00527                 {
00528                     Node.save();
00529                     ++n_written;
00530                 }
00531                 else
00532                     ++n_clean_forced;
00533 
00534 
00535                 BID2node_.erase(Node.my_bid());
00536 
00537                 reqs_[node2kick] = Node.prefetch(bid);
00538                 BID2node_[bid] = node2kick;
00539 
00540                 fixed_[node2kick] = false;
00541 
00542                 dirty_[node2kick] = false;
00543 
00544                 assert(size() == BID2node_.size() + free_nodes_.size());
00545 
00546                 STXXL_VERBOSE1("btree::node_cache prefetch_node, need to kick node" << node2kick << " ");
00547 
00548                 return;
00549             }
00550 
00551             int_type free_node = free_nodes_.back();
00552             free_nodes_.pop_back();
00553             assert(fixed_[free_node] == false);
00554 
00555             node_type & Node = *(nodes_[free_node]);
00556             reqs_[free_node] = Node.prefetch(bid);
00557             BID2node_[bid] = free_node;
00558 
00559             pager_.hit(free_node);
00560 
00561             fixed_[free_node] = false;
00562 
00563             dirty_[free_node] = false;
00564 
00565             assert(size() == BID2node_.size() + free_nodes_.size());
00566 
00567             STXXL_VERBOSE1("btree::node_cache prefetch_node, free node " << free_node << "available");
00568 
00569             return;
00570         }
00571 
00572         void unfix_node(const bid_type & bid)
00573         {
00574             assert(BID2node_.find(bid) != BID2node_.end());
00575             fixed_[BID2node_[bid]] = false;
00576             STXXL_VERBOSE1("btree::node_cache unfix_node,  node " << BID2node_[bid]);
00577         }
00578 
00579         void swap(node_cache & obj)
00580         {
00581             std::swap(comp_, obj.comp_);
00582             std::swap(nodes_, obj.nodes_);
00583             std::swap(reqs_, obj.reqs_);
00584             change_btree_pointers(btree_);
00585             obj.change_btree_pointers(obj.btree_);
00586             std::swap(fixed_, obj.fixed_);
00587             std::swap(free_nodes_, obj.free_nodes_);
00588             std::swap(BID2node_, obj.BID2node_);
00589             std::swap(pager_, obj.pager_);
00590             std::swap(alloc_strategy_, obj.alloc_strategy_);
00591             std::swap(n_found, obj.n_found);
00592             std::swap(n_not_found, obj.n_found);
00593             std::swap(n_created, obj.n_created);
00594             std::swap(n_deleted, obj.n_deleted);
00595             std::swap(n_read, obj.n_read);
00596             std::swap(n_written, obj.n_written);
00597             std::swap(n_clean_forced, obj.n_clean_forced);
00598         }
00599 
00600         void print_statistics(std::ostream & o) const
00601         {
00602             if (n_read)
00603                 o << "Found blocks                      : " << n_found << " (" <<
00604                 100. * double(n_found) / double(n_read) << "%)" << std::endl;
00605 
00606             else
00607                 o << "Found blocks                      : " << n_found << " (" <<
00608                 100 << "%)" << std::endl;
00609 
00610             o << "Not found blocks                  : " << n_not_found << std::endl;
00611             o << "Created in the cache blocks       : " << n_created << std::endl;
00612             o << "Deleted blocks                    : " << n_deleted << std::endl;
00613             o << "Read blocks                       : " << n_read << std::endl;
00614             o << "Written blocks                    : " << n_written << std::endl;
00615             o << "Clean blocks forced from the cache: " << n_clean_forced << std::endl;
00616         }
00617         void reset_statistics()
00618         {
00619             n_found = 0;
00620             n_not_found = 0;
00621             n_created = 0;
00622             n_deleted = 0;
00623             n_read = 0;
00624             n_written = 0;
00625             n_clean_forced = 0;
00626         }
00627     };
00628 }
00629 
00630 __STXXL_END_NAMESPACE
00631 
00632 
00633 namespace std
00634 {
00635     template <class NodeType, class BTreeType>
00636     void swap(stxxl::btree::node_cache<NodeType, BTreeType> & a,
00637               stxxl::btree::node_cache<NodeType, BTreeType> & b)
00638     {
00639         a.swap(b);
00640     }
00641 }
00642 
00643 #endif /* STXXL_CONTAINERS_BTREE__NODE_CACHE_H */

Generated by  doxygen 1.7.1