Stxxl
1.4.0
|
00001 /*************************************************************************** 00002 * include/stxxl/bits/mng/block_scheduler.h 00003 * 00004 * Part of the STXXL. See http://stxxl.sourceforge.net 00005 * 00006 * Copyright (C) 2010-2011 Raoul Steffen <R-Steffen@gmx.de> 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_BLOCK_SCHEDULER_HEADER 00014 #define STXXL_BLOCK_SCHEDULER_HEADER 00015 00016 #include <stack> 00017 #include <queue> 00018 #include <limits> 00019 00020 #include <stxxl/bits/mng/mng.h> 00021 #include <stxxl/bits/mng/typed_block.h> 00022 #include <stxxl/bits/common/addressable_queues.h> 00023 00024 __STXXL_BEGIN_NAMESPACE 00025 00026 //! \brief Virtualization of a block of data. 00027 //! Holds information for allocating and swapping. To use in cooperation with block_scheduler. 00028 //! 00029 //! A swappable_block can be uninitialized, i.e. it holds no data. 00030 //! When access is required, is has to be acquired first, and released afterwards, so it can be swapped in and out as required. 00031 //! If the stored data is no longer needed, it can get uninitialized, freeing both internal and external memory. 00032 //! \tparam ValueType type of contained objects (POD with no references to internal memory). 00033 //! \tparam BlockSize Number of objects in one block. 00034 //! BlockSize*sizeof(ValueType) must be divisible by 4096. 00035 template <typename ValueType, unsigned BlockSize> 00036 class swappable_block 00037 { 00038 protected: 00039 static const unsigned_type raw_block_size = BlockSize * sizeof(ValueType); 00040 public: 00041 typedef typed_block<raw_block_size, ValueType> internal_block_type; 00042 typedef typename internal_block_type::bid_type external_block_type; 00043 00044 protected: 00045 external_block_type external_data; //!external_data.valid if no associated space on disk 00046 internal_block_type * internal_data; //NULL if there is no internal memory reserved 00047 bool dirty; 00048 int_type reference_count; 00049 00050 static unsigned_type disk_allocation_offset; 00051 00052 void get_external_block() 00053 { block_manager::get_instance()->new_block(striping(), external_data, ++disk_allocation_offset); } 00054 00055 void free_external_block() 00056 { 00057 block_manager::get_instance()->delete_block(external_data); 00058 external_data = external_block_type(); // make invalid 00059 } 00060 00061 public: 00062 //! \brief Create in uninitialized state. 00063 swappable_block() 00064 : external_data() /*!valid*/, internal_data(0), dirty(false), reference_count(0) {} 00065 00066 ~swappable_block() {} 00067 00068 //! \brief If it has an internal_block. The internal_block implicitly holds valid data. 00069 bool is_internal() const 00070 { return internal_data; } 00071 00072 //! \brief If the external_block does not hold valid data. 00073 bool is_dirty() const 00074 { return dirty; } 00075 00076 //! \brief If it has an external_block. 00077 bool has_external_block() const 00078 { return external_data.valid(); } 00079 00080 //! \brief If it has an external_block that holds valid data. 00081 bool is_external() const 00082 { return has_external_block() && ! is_dirty(); } 00083 00084 //! \brief If it is acquired. 00085 bool is_acquired() const 00086 { return reference_count > 0; } 00087 00088 //! \brief If it holds an internal_block but does not need it. 00089 bool is_evictable() const 00090 { return ! is_acquired() && is_internal(); } 00091 00092 //! \brief If it has some valid data (in- or external). 00093 bool is_initialized() const 00094 { return is_internal() || is_external(); } 00095 00096 //! \brief Invalidate external data if true. 00097 //! \return is_dirty() 00098 bool make_dirty_if(const bool make_dirty) 00099 { 00100 assert(is_acquired()); 00101 return dirty = make_dirty || dirty; 00102 } 00103 00104 //! \brief Acquire the block, i.e. add a reference. Has to be internal. 00105 //! \return A reference to the data-block. 00106 internal_block_type & acquire() 00107 { 00108 assert(is_internal()); 00109 ++ reference_count; 00110 return * internal_data; 00111 } 00112 00113 //! \brief Release the block, i.e. subduct a reference. Has to be acquired. 00114 void release() 00115 { 00116 assert(is_acquired()); 00117 -- reference_count; 00118 } 00119 00120 //! \brief Get a reference to the data-block. Has to be acquired. 00121 const internal_block_type & get_internal_block() const 00122 { 00123 assert(is_acquired()); 00124 return * internal_data; 00125 } 00126 00127 //! \brief Get a reference to the data-block. Has to be acquired. 00128 internal_block_type & get_internal_block() 00129 { 00130 assert(is_acquired()); 00131 return * internal_data; 00132 } 00133 00134 //! \brief Fill block with default data, is supposed to be overwritten by subclass. Has to be internal. 00135 void fill_default() {} 00136 00137 //! \brief Read asyncronusly from external_block to internal_block. Has to be internal and have an external_block. 00138 //! \return A request pointer to the I/O. 00139 request_ptr read_async(completion_handler on_cmpl = default_completion_handler()) 00140 { 00141 assert(is_internal()); 00142 assert(has_external_block()); 00143 #ifdef RW_VERBOSE 00144 STXXL_MSG("reading block"); 00145 #endif 00146 dirty = false; 00147 return internal_data->read(external_data, on_cmpl); 00148 } 00149 00150 //! \brief Read synchronously from external_block to internal_block. Has to be internal and have an external_block. 00151 void read_sync() 00152 { read_async()->wait(); } 00153 00154 //! \brief Write asyncronusly from internal_block to external_block if necessary. 00155 //! \return A request pointer to the I/O, an invalid request pointer if not necessary. 00156 request_ptr clean_async(completion_handler on_cmpl = default_completion_handler()) 00157 { 00158 if (! is_dirty()) 00159 return request_ptr(); 00160 if (! has_external_block()) 00161 get_external_block(); 00162 #ifdef RW_VERBOSE 00163 STXXL_MSG("writing block"); 00164 #endif 00165 dirty = false; 00166 return internal_data->write(external_data, on_cmpl); 00167 } 00168 00169 //! \brief Write synchronously from internal_block to external_block if necessary. 00170 void clean_sync() 00171 { 00172 request_ptr rp = clean_async(); 00173 if (rp.valid()) 00174 rp->wait(); 00175 } 00176 00177 //! \brief Attach an internal_block, making the block internal. Has to be not internal. 00178 void attach_internal_block(internal_block_type * iblock) 00179 { 00180 assert(! is_internal()); 00181 internal_data = iblock; 00182 } 00183 00184 //! \brief Detach the internal_block. Writes to external_block if necessary. Has to be evictable. 00185 //! \return A pointer to the internal_block. 00186 internal_block_type * detach_internal_block() 00187 { 00188 assert(is_evictable()); 00189 clean_sync(); 00190 internal_block_type * iblock = internal_data; 00191 internal_data = 0; 00192 return iblock; 00193 } 00194 00195 //! \brief Bring the block in uninitialized state, freeing external and internal memory. 00196 //! Returns a pointer to the internal_block, NULL if it had none. 00197 //! \return A pointer to the freed internal_block, NULL if it had none. 00198 internal_block_type * deinitialize() 00199 { 00200 assert(! is_acquired()); 00201 dirty = false; 00202 // free external_block (so that it becomes invalid and the disk-space can be used again) 00203 if (has_external_block()) 00204 free_external_block(); 00205 // free internal_block 00206 internal_block_type * iblock = internal_data; 00207 internal_data = 0; 00208 return iblock; 00209 } 00210 00211 //! \brief Set the external_block that holds the swappable_block's data. The block gets initialized with it. 00212 //! \param eblock The external_block holding initial data. 00213 void initialize(external_block_type eblock) 00214 { 00215 assert(! is_initialized()); 00216 external_data = eblock; 00217 } 00218 00219 //! \brief Extract the swappable_blocks data in an external_block. The block gets uninitialized. 00220 //! \return The external_block that holds the swappable_block's data. 00221 external_block_type extract_external_block() 00222 { 00223 assert(! is_internal()); 00224 external_block_type eblock = external_data; 00225 external_data = external_block_type(); 00226 return eblock; 00227 } 00228 }; 00229 00230 template <typename ValueType, unsigned BlockSize> 00231 unsigned_type swappable_block<ValueType, BlockSize>::disk_allocation_offset = 0; 00232 00233 template <class SwappableBlockType> class block_scheduler_algorithm; 00234 template <class SwappableBlockType> class block_scheduler_algorithm_online_lru; 00235 00236 //! \brief Schedules swapping of blocks and provides blocks for temporary storage. 00237 //! 00238 //! In simple mode, it tries to save I/Os through caching only. 00239 //! In simulation mode, it records access patterns into a prediction sequence. 00240 //! The prediction sequence can then be used for prefetching in the (offline) execute mode. 00241 //! This will only work for algorithms with deterministic, data oblivious access patterns. 00242 //! In simulation mode, no I/O is performed; the data provided is accessible but undefined. 00243 //! In execute mode, it does caching, prefetching, and possibly other optimizations. 00244 //! \tparam SwappableBlockType Type of swappable_blocks to manage. Can be some specialized subclass. 00245 template <class SwappableBlockType> 00246 class block_scheduler : private noncopyable 00247 { 00248 protected: 00249 // tuning-parameter: To acquire blocks, internal memory has to be allocated. 00250 // This constant limits the number of internal_blocks allocated at once. 00251 static const int_type max_internal_blocks_alloc_at_once; 00252 00253 typedef int_type time_type; 00254 00255 public: 00256 typedef typename SwappableBlockType::internal_block_type internal_block_type; 00257 typedef typename SwappableBlockType::external_block_type external_block_type; 00258 typedef typename std::vector<SwappableBlockType>::size_type swappable_block_identifier_type; 00259 00260 /*/! Mode the block scheduler currently works in 00261 enum mode 00262 { 00263 online, //serve requests immediately, without any prediction, LRU caching 00264 simulation, //record prediction sequence only, do not serve requests, (returned blocks must not be accessed) 00265 offline_lfd, //serve requests based on prediction sequence, using longest-forward-distance caching 00266 offline_lfd_prefetch //serve requests based on prediction sequence, using longest-forward-distance caching, and prefetching 00267 };*/ 00268 00269 public: 00270 // -------- prediction_sequence ------- 00271 enum block_scheduler_operation 00272 { 00273 op_acquire, 00274 op_acquire_uninitialized, 00275 op_release, 00276 op_release_dirty, 00277 op_deinitialize, 00278 op_initialize, 00279 op_extract_external_block 00280 }; 00281 00282 struct prediction_sequence_element 00283 { 00284 block_scheduler_operation op; 00285 swappable_block_identifier_type id; 00286 time_type time; 00287 00288 prediction_sequence_element(block_scheduler_operation op, 00289 swappable_block_identifier_type id, int_type time) 00290 : op(op), id(id), time(time) {} 00291 00292 }; 00293 00294 typedef std::deque<prediction_sequence_element> prediction_sequence_type; 00295 // ---- end prediction_sequence ------- 00296 00297 protected: 00298 template <class SBT> friend class block_scheduler_algorithm; 00299 00300 const int_type max_internal_blocks; 00301 int_type remaining_internal_blocks; 00302 //! \brief Stores pointers to arrays of internal_blocks. Used to deallocate them only. 00303 std::stack<internal_block_type *> internal_blocks_blocks; 00304 //! \brief holds free internal_blocks with attributes reset 00305 std::stack<internal_block_type * > free_internal_blocks; 00306 //! \brief temporary blocks that will not be needed after algorithm termination 00307 mutable std::vector<SwappableBlockType> swappable_blocks; 00308 //! \brief holds indices of free swappable_blocks with attributes reset 00309 std::priority_queue<swappable_block_identifier_type, std::vector<swappable_block_identifier_type>, 00310 std::greater<swappable_block_identifier_type> > free_swappable_blocks; 00311 block_manager * bm; 00312 block_scheduler_algorithm<SwappableBlockType> * algo; 00313 00314 //! \brief Get an internal_block from the freelist or a newly allocated one if available. 00315 //! \return Pointer to the internal_block. NULL if none available. 00316 internal_block_type * get_free_internal_block() 00317 { 00318 if (! free_internal_blocks.empty()) 00319 { 00320 // => there are internal_blocks in the free-list 00321 internal_block_type * iblock = free_internal_blocks.top(); 00322 free_internal_blocks.pop(); 00323 return iblock; 00324 } 00325 else if (remaining_internal_blocks > 0) 00326 { 00327 // => more internal_blocks can be allocated 00328 int_type num_blocks = std::min(max_internal_blocks_alloc_at_once, remaining_internal_blocks); 00329 remaining_internal_blocks -= num_blocks; 00330 internal_block_type * iblocks = new internal_block_type[num_blocks]; 00331 internal_blocks_blocks.push(iblocks); 00332 for (int_type i = num_blocks - 1; i > 0; --i) 00333 free_internal_blocks.push(iblocks + i); 00334 return iblocks; 00335 } 00336 else 00337 { 00338 // => no internal_block available 00339 return 0; 00340 } 00341 } 00342 00343 //! \brief Return an internal_block to the freelist. 00344 void return_free_internal_block(internal_block_type * iblock) 00345 { free_internal_blocks.push(iblock); } 00346 00347 public: 00348 //! \brief create a block_scheduler with empty prediction sequence in simple mode. 00349 //! \param max_internal_memory Amount of internal memory (in bytes) the scheduler is allowed to use for acquiring, prefetching and caching. 00350 explicit block_scheduler(const int_type max_internal_memory) 00351 : max_internal_blocks(div_ceil(max_internal_memory, sizeof(internal_block_type))), 00352 remaining_internal_blocks(max_internal_blocks), 00353 bm(block_manager::get_instance()), 00354 algo(0) 00355 { algo = new block_scheduler_algorithm_online_lru<SwappableBlockType>(*this); } 00356 00357 ~block_scheduler() 00358 { 00359 delete algo; 00360 int_type num_freed_internal_blocks = 0; 00361 if (free_swappable_blocks.size() != swappable_blocks.size()) 00362 { 00363 // => not all swappable_blocks are free, at least deinitialize them 00364 STXXL_ERRMSG("not all swappable_blocks are free, those not acquired will be deinitialized"); 00365 for (typename std::vector<SwappableBlockType>::iterator it = swappable_blocks.begin(); // evictable_blocks would suffice 00366 it != swappable_blocks.end(); ++it) 00367 if (! it->is_acquired()) 00368 num_freed_internal_blocks += bool(it->deinitialize()); // count internal_blocks that get freed 00369 } 00370 if (int_type nlost = (max_internal_blocks - remaining_internal_blocks) 00371 - (free_internal_blocks.size() + num_freed_internal_blocks)) 00372 STXXL_ERRMSG(nlost << " internal_blocks are lost. They will get deallocated."); 00373 while (! internal_blocks_blocks.empty()) 00374 { 00375 delete [] internal_blocks_blocks.top(); 00376 internal_blocks_blocks.pop(); 00377 } 00378 } 00379 00380 //! \brief Acquire the given block. 00381 //! Has to be in pairs with release. Pairs may be nested and interleaved. 00382 //! \return Reference to the block's data. 00383 //! \param sblock Swappable block to acquire. 00384 internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) 00385 { return algo->acquire(sbid, uninitialized); } 00386 00387 //! \brief Release the given block. 00388 //! Has to be in pairs with acquire. Pairs may be nested and interleaved. 00389 //! \param sblock Swappable block to release. 00390 //! \param dirty If the data has been changed, invalidating possible data in external storage. 00391 void release(const swappable_block_identifier_type sbid, const bool dirty) 00392 { algo->release(sbid, dirty); } 00393 00394 //! \brief Drop all data in the given block, freeing in- and external memory. 00395 void deinitialize(const swappable_block_identifier_type sbid) 00396 { algo->deinitialize(sbid); } 00397 00398 //! \brief Initialize the swappable_block with the given external_block. 00399 //! 00400 //! It will use the the external_block for swapping and take care about it's deallocation. Has to be uninitialized. 00401 //! \param sbid identifier to the swappable_block 00402 //! \param eblock external_block a.k.a. bid 00403 void initialize(const swappable_block_identifier_type sbid, external_block_type eblock) 00404 { algo->initialize(sbid, eblock); } 00405 00406 //! \brief deinitialize the swappable_block and return it's contents in an external_block. 00407 //! 00408 //! \param sbid identifier to the swappable_block 00409 //! \return external_block a.k.a. bid 00410 external_block_type extract_external_block(const swappable_block_identifier_type sbid) 00411 { return algo->extract_external_block(sbid); } 00412 00413 //! \brief check if the swappable_block is initialized 00414 //! \param sbid identifier to the swappable_block 00415 //! \return if the swappable_block is initialized 00416 bool is_initialized(const swappable_block_identifier_type sbid) const 00417 { return algo->is_initialized(sbid); } 00418 00419 //! \brief Record a timestep in the prediction sequence to seperate consecutive acquire rsp. release-operations. 00420 //! Has an effect only in simulation mode. 00421 void explicit_timestep() 00422 { algo->explicit_timestep(); } 00423 00424 //! \brief Get a const reference to given block's data. Block has to be already acquired. 00425 //! \param sblock Swappable block to access. 00426 internal_block_type & get_internal_block(const swappable_block_identifier_type sbid) const 00427 { return swappable_blocks[sbid].get_internal_block(); } 00428 00429 //! \brief Allocate an uninitialized swappable_block. 00430 //! \return An identifier of the block. 00431 swappable_block_identifier_type allocate_swappable_block() 00432 { 00433 swappable_block_identifier_type sbid; 00434 if (free_swappable_blocks.empty()) 00435 { 00436 // create new swappable_block 00437 sbid = swappable_blocks.size(); 00438 swappable_blocks.resize(sbid + 1); 00439 algo->swappable_blocks_resize(sbid + 1); 00440 } 00441 else 00442 { 00443 // take swappable_block from freelist 00444 sbid = free_swappable_blocks.top(); 00445 free_swappable_blocks.pop(); 00446 } 00447 return sbid; 00448 } 00449 00450 //! \brief Free given no longer used temporary swappable_block. 00451 //! \param sblock Temporary swappable_block to free. 00452 void free_swappable_block(const swappable_block_identifier_type sbid) 00453 { 00454 deinitialize(sbid); 00455 free_swappable_blocks.push(sbid); 00456 } 00457 00458 //! \brief Returns if simulation mode is on, i.e. if a prediction sequence is being recorded. 00459 //! \return If simulation mode is on. 00460 bool is_simulating() const 00461 { return algo->is_simulating(); } 00462 00463 //! \brief Switch the used algorithm, e.g. to simulation etc.. 00464 //! \param new_algo Pointer to the new algorithm object. Has to be instantiated to the block scheduler (or the old algorithm object). 00465 //! \return Pointer to the old algorithm object. 00466 block_scheduler_algorithm<SwappableBlockType> * switch_algorithm_to(block_scheduler_algorithm<SwappableBlockType> * new_algo) 00467 { 00468 assert(&new_algo->bs == this); 00469 block_scheduler_algorithm<SwappableBlockType> * old_algo = algo; 00470 algo = new_algo; 00471 return old_algo; 00472 } 00473 00474 //! \brief get the prediction_sequence 00475 //! \return reference to the prediction_sequence 00476 const prediction_sequence_type & get_prediction_sequence() const 00477 { return algo->get_prediction_sequence(); } 00478 00479 void flush() 00480 { 00481 std::vector<request_ptr> requests; 00482 while (! algo->evictable_blocks_empty()) 00483 { 00484 swappable_block_identifier_type sbid = algo->evictable_blocks_pop(); 00485 request_ptr rq = swappable_blocks[sbid].clean_async(); 00486 if (rq.valid()) 00487 requests.push_back(rq); 00488 return_free_internal_block(swappable_blocks[sbid].detach_internal_block()); 00489 } 00490 for (typename std::vector<request_ptr>::reverse_iterator it = requests.rbegin(); 00491 it != requests.rend(); ++it) 00492 (*it)->wait(); 00493 } 00494 }; 00495 00496 template <class SwappableBlockType> 00497 const int_type block_scheduler<SwappableBlockType>::max_internal_blocks_alloc_at_once = 128; 00498 00499 //! \brief Interface of a block scheduling algorithm. 00500 template <class SwappableBlockType> 00501 class block_scheduler_algorithm : private noncopyable 00502 { 00503 protected: 00504 typedef block_scheduler<SwappableBlockType> block_scheduler_type; 00505 typedef typename block_scheduler_type::internal_block_type internal_block_type; 00506 typedef typename block_scheduler_type::external_block_type external_block_type; 00507 typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; 00508 typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; 00509 typedef typename block_scheduler_type::time_type time_type; 00510 00511 public: 00512 block_scheduler_type & bs; 00513 protected: 00514 std::vector<SwappableBlockType> & swappable_blocks; 00515 prediction_sequence_type prediction_sequence; 00516 00517 block_scheduler_algorithm * get_algorithm_from_block_scheduler() 00518 { return bs.algo; } 00519 00520 //! \brief Get an internal_block from the block_scheduler. 00521 //! \return Pointer to the internal_block. NULL if none available. 00522 internal_block_type * get_free_internal_block_from_block_scheduler() 00523 { return bs.get_free_internal_block(); } 00524 00525 //! \brief Return an internal_block to the block_scheduler. 00526 void return_free_internal_block_to_block_scheduler(internal_block_type * iblock) 00527 { bs.return_free_internal_block(iblock); } 00528 00529 public: 00530 block_scheduler_algorithm(block_scheduler_type & bs) 00531 : bs(bs), 00532 swappable_blocks(bs.swappable_blocks) 00533 {} 00534 00535 block_scheduler_algorithm(block_scheduler_algorithm * old) 00536 : bs(old->bs), 00537 swappable_blocks(bs.swappable_blocks) 00538 {} 00539 00540 virtual ~block_scheduler_algorithm() {}; 00541 00542 virtual bool evictable_blocks_empty() = 0; 00543 virtual swappable_block_identifier_type evictable_blocks_pop() = 0; 00544 virtual void swappable_blocks_resize(swappable_block_identifier_type /*size*/) {} 00545 00546 virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) = 0; 00547 virtual void release(swappable_block_identifier_type sbid, const bool dirty) = 0; 00548 virtual void deinitialize(swappable_block_identifier_type sbid) = 0; 00549 virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) = 0; 00550 virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) = 0; 00551 00552 virtual bool is_initialized(const swappable_block_identifier_type sbid) const 00553 { return swappable_blocks[sbid].is_initialized(); } 00554 00555 virtual void explicit_timestep() {} 00556 virtual bool is_simulating() const 00557 { return false; } 00558 virtual const prediction_sequence_type & get_prediction_sequence() const 00559 { return prediction_sequence; } 00560 }; 00561 00562 //! \brief Block scheduling algorithm caching via the least recently used policy (online). 00563 template <class SwappableBlockType> 00564 class block_scheduler_algorithm_online_lru : public block_scheduler_algorithm<SwappableBlockType> 00565 { 00566 protected: 00567 typedef block_scheduler<SwappableBlockType> block_scheduler_type; 00568 typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type; 00569 typedef typename block_scheduler_type::internal_block_type internal_block_type; 00570 typedef typename block_scheduler_type::external_block_type external_block_type; 00571 typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; 00572 00573 using block_scheduler_algorithm_type::bs; 00574 using block_scheduler_algorithm_type::swappable_blocks; 00575 using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; 00576 using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; 00577 using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; 00578 00579 //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. 00580 addressable_fifo_queue<swappable_block_identifier_type> evictable_blocks; 00581 00582 internal_block_type * get_free_internal_block() 00583 { 00584 // try to get a free internal_block 00585 if (internal_block_type * iblock = get_free_internal_block_from_block_scheduler()) 00586 return iblock; 00587 // evict block 00588 assert(! evictable_blocks.empty()); // fails it there is not enough memory available 00589 return swappable_blocks[evictable_blocks.pop()].detach_internal_block(); 00590 } 00591 00592 void return_free_internal_block(internal_block_type * iblock) 00593 { return_free_internal_block_to_block_scheduler(iblock); } 00594 00595 void init() 00596 { 00597 if (get_algorithm_from_block_scheduler()) 00598 while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) 00599 evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); 00600 } 00601 00602 public: 00603 block_scheduler_algorithm_online_lru(block_scheduler_type & bs) 00604 : block_scheduler_algorithm_type(bs) 00605 { init(); } 00606 00607 block_scheduler_algorithm_online_lru(block_scheduler_algorithm_type * old) 00608 : block_scheduler_algorithm_type(old) 00609 { init(); } 00610 00611 virtual ~block_scheduler_algorithm_online_lru() 00612 { 00613 if (! evictable_blocks.empty()) 00614 STXXL_ERRMSG("Destructing block_scheduler_algorithm_online that still holds evictable blocks. They get deinitialized."); 00615 while (! evictable_blocks.empty()) 00616 { 00617 SwappableBlockType & sblock = swappable_blocks[evictable_blocks.pop()]; 00618 if (internal_block_type * iblock = sblock.deinitialize()) 00619 return_free_internal_block(iblock); 00620 } 00621 } 00622 00623 virtual bool evictable_blocks_empty() 00624 { return evictable_blocks.empty(); } 00625 00626 virtual swappable_block_identifier_type evictable_blocks_pop() 00627 { return evictable_blocks.pop(); } 00628 00629 virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) 00630 { 00631 SwappableBlockType & sblock = swappable_blocks[sbid]; 00632 /* acquired => internal -> increase reference count 00633 internal but not acquired -> remove from evictable_blocks, increase reference count 00634 not internal => uninitialized or external -> get internal_block, increase reference count 00635 uninitialized -> fill with default value 00636 external -> read */ 00637 if (sblock.is_internal()) 00638 { 00639 if (! sblock.is_acquired()) 00640 // not acquired yet -> remove from evictable_blocks 00641 evictable_blocks.erase(sbid); 00642 sblock.acquire(); 00643 } 00644 else if (sblock.is_initialized()) 00645 { 00646 // => external but not internal 00647 //get internal_block 00648 sblock.attach_internal_block(get_free_internal_block()); 00649 if (! uninitialized) 00650 //load block synchronously 00651 sblock.read_sync(); 00652 sblock.acquire(); 00653 } 00654 else 00655 { 00656 // => ! sblock.is_initialized() 00657 //get internal_block 00658 sblock.attach_internal_block(get_free_internal_block()); 00659 sblock.acquire(); 00660 //initialize new block 00661 if (! uninitialized) 00662 sblock.fill_default(); 00663 } 00664 return sblock.get_internal_block(); 00665 } 00666 00667 virtual void release(swappable_block_identifier_type sbid, const bool dirty) 00668 { 00669 SwappableBlockType & sblock = swappable_blocks[sbid]; 00670 sblock.make_dirty_if(dirty); 00671 sblock.release(); 00672 if (! sblock.is_acquired()) 00673 { 00674 if (sblock.is_dirty() || sblock.is_external()) 00675 // => evictable, put in pq 00676 evictable_blocks.insert(sbid); 00677 else 00678 // => uninitialized, release internal block and put it in freelist 00679 return_free_internal_block(sblock.detach_internal_block()); 00680 } 00681 } 00682 00683 virtual void deinitialize(swappable_block_identifier_type sbid) 00684 { 00685 SwappableBlockType & sblock = swappable_blocks[sbid]; 00686 if (sblock.is_evictable()) 00687 evictable_blocks.erase(sbid); 00688 if (internal_block_type * iblock = sblock.deinitialize()) 00689 return_free_internal_block(iblock); 00690 } 00691 00692 virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) 00693 { 00694 SwappableBlockType & sblock = swappable_blocks[sbid]; 00695 sblock.initialize(eblock); 00696 } 00697 00698 virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) 00699 { 00700 SwappableBlockType & sblock = swappable_blocks[sbid]; 00701 if (sblock.is_evictable()) 00702 evictable_blocks.erase(sbid); 00703 if (sblock.is_internal()) 00704 return_free_internal_block(sblock.detach_internal_block()); 00705 return sblock.extract_external_block(); 00706 } 00707 }; 00708 00709 //! \brief Pseudo block scheduling algorithm only recording the request sequence. 00710 template <class SwappableBlockType> 00711 class block_scheduler_algorithm_simulation : public block_scheduler_algorithm<SwappableBlockType> 00712 { 00713 protected: 00714 typedef block_scheduler<SwappableBlockType> block_scheduler_type; 00715 typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type; 00716 typedef typename block_scheduler_type::internal_block_type internal_block_type; 00717 typedef typename block_scheduler_type::external_block_type external_block_type; 00718 typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; 00719 typedef typename block_scheduler_type::prediction_sequence_element prediction_sequence_element_type; 00720 typedef typename block_scheduler_algorithm_type::time_type time_type; 00721 00722 using block_scheduler_algorithm_type::bs; 00723 using block_scheduler_algorithm_type::prediction_sequence; 00724 using block_scheduler_algorithm_type::swappable_blocks; 00725 using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; 00726 using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; 00727 using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; 00728 00729 //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. 00730 std::stack<swappable_block_identifier_type> evictable_blocks; 00731 time_type time_count; 00732 bool last_op_release; 00733 std::vector<int_type> reference_counts; 00734 internal_block_type dummy_block; 00735 00736 void return_free_internal_block(internal_block_type * iblock) 00737 { return_free_internal_block_to_block_scheduler(iblock); } 00738 00739 void init() 00740 { 00741 if (get_algorithm_from_block_scheduler()) 00742 while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) 00743 evictable_blocks.push(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); 00744 for (swappable_block_identifier_type i = 0; i < reference_counts.size(); ++i) 00745 reference_counts[i] = swappable_blocks[i].is_initialized(); 00746 } 00747 00748 public: 00749 block_scheduler_algorithm_simulation(block_scheduler_type & bs) 00750 : block_scheduler_algorithm_type(bs), 00751 time_count(0), 00752 last_op_release(false), 00753 reference_counts(swappable_blocks.size()) 00754 { init(); } 00755 00756 block_scheduler_algorithm_simulation(block_scheduler_algorithm_type * old) 00757 : block_scheduler_algorithm_type(old), 00758 time_count(0), 00759 last_op_release(false), 00760 reference_counts(swappable_blocks.size()) 00761 { init(); } 00762 00763 virtual ~block_scheduler_algorithm_simulation() 00764 { 00765 if (! evictable_blocks.empty()) 00766 STXXL_ERRMSG("Destructing block_scheduler_algorithm_record_prediction_sequence that still holds evictable blocks. They get deinitialized."); 00767 while (! evictable_blocks.empty()) 00768 { 00769 SwappableBlockType & sblock = swappable_blocks[evictable_blocks.top()]; 00770 if (internal_block_type * iblock = sblock.deinitialize()) 00771 return_free_internal_block(iblock); 00772 evictable_blocks.pop(); 00773 } 00774 } 00775 00776 virtual bool evictable_blocks_empty() 00777 { return evictable_blocks.empty(); } 00778 00779 virtual swappable_block_identifier_type evictable_blocks_pop() 00780 { 00781 swappable_block_identifier_type sbid = evictable_blocks.top(); 00782 evictable_blocks.pop(); 00783 return sbid; 00784 } 00785 00786 virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) 00787 { 00788 ++reference_counts[sbid]; 00789 last_op_release = false; 00790 if (uninitialized) 00791 prediction_sequence.push_back(prediction_sequence_element_type 00792 (block_scheduler_type::op_acquire_uninitialized, sbid, time_count)); 00793 else 00794 prediction_sequence.push_back(prediction_sequence_element_type 00795 (block_scheduler_type::op_acquire, sbid, time_count)); 00796 return dummy_block; 00797 } 00798 00799 virtual void release(swappable_block_identifier_type sbid, const bool dirty) 00800 { 00801 --reference_counts[sbid] += dirty; 00802 time_count += ! last_op_release; 00803 last_op_release = true; 00804 if (dirty) 00805 prediction_sequence.push_back(prediction_sequence_element_type 00806 (block_scheduler_type::op_release_dirty, sbid, time_count)); 00807 else 00808 prediction_sequence.push_back(prediction_sequence_element_type 00809 (block_scheduler_type::op_release, sbid, time_count)); 00810 } 00811 00812 virtual void deinitialize(swappable_block_identifier_type sbid) 00813 { 00814 reference_counts[sbid] = false; 00815 prediction_sequence.push_back(prediction_sequence_element_type 00816 (block_scheduler_type::op_deinitialize, sbid, time_count)); 00817 } 00818 00819 virtual void initialize(swappable_block_identifier_type sbid, external_block_type) 00820 { 00821 reference_counts[sbid] = true; 00822 prediction_sequence.push_back(prediction_sequence_element_type 00823 (block_scheduler_type::op_initialize, sbid, time_count)); 00824 } 00825 00826 virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) 00827 { 00828 reference_counts[sbid] = false; 00829 prediction_sequence.push_back(prediction_sequence_element_type 00830 (block_scheduler_type::op_extract_external_block, sbid, time_count)); 00831 return external_block_type(); 00832 } 00833 00834 virtual void swappable_blocks_resize(swappable_block_identifier_type size) 00835 { 00836 reference_counts.resize(size, 0); 00837 } 00838 00839 virtual bool is_initialized(const swappable_block_identifier_type sbid) const 00840 { 00841 return reference_counts[sbid] > 0; 00842 } 00843 00844 virtual void explicit_timestep() 00845 { ++time_count; }; 00846 00847 virtual bool is_simulating() const 00848 { return true; } 00849 }; 00850 00851 //! \brief Block scheduling algorithm caching via the longest forward distance policy (offline). 00852 template <class SwappableBlockType> 00853 class block_scheduler_algorithm_offline_lfd : public block_scheduler_algorithm<SwappableBlockType> 00854 { 00855 protected: 00856 typedef block_scheduler<SwappableBlockType> block_scheduler_type; 00857 typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type; 00858 typedef typename block_scheduler_type::internal_block_type internal_block_type; 00859 typedef typename block_scheduler_type::external_block_type external_block_type; 00860 typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; 00861 typedef typename block_scheduler_algorithm_type::time_type time_type; 00862 typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; 00863 00864 using block_scheduler_algorithm_type::bs; 00865 using block_scheduler_algorithm_type::swappable_blocks; 00866 using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; 00867 using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; 00868 using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; 00869 00870 class priority 00871 { 00872 unsigned_type p; 00873 00874 public: 00875 priority(const SwappableBlockType & sblock, const std::pair<bool, time_type> & t) 00876 { 00877 // p larger => evict earlier 00878 if (t.first) 00879 { 00880 // most significant: next use 00881 p = unsigned_type(t.second) << 2; 00882 // less significant: not dirty 00883 p |= unsigned_type(! sblock.is_dirty()) << 1; 00884 // less significant: has external_block 00885 p |= unsigned_type(sblock.has_external_block()) << 0; 00886 } 00887 else 00888 { 00889 // most significant: next use 00890 p = std::numeric_limits<unsigned_type>::max() << 2; 00891 // less significant: next operation: extract > accessed no more > deinitialize 00892 p |= unsigned_type(t.second) << 0; 00893 } 00894 } 00895 00896 // less => evict earlier 00897 bool operator < (const priority & right) const 00898 { return p > right.p; } 00899 }; 00900 00901 //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. 00902 addressable_priority_queue<swappable_block_identifier_type, priority> evictable_blocks; 00903 /*! \brief stores for the sequence of releases extracted from the prediction_sequence: 00904 (true, timestamp of the blocks next acquire) if it is acquired next 00905 (false, 0) if it is deinitialized next 00906 (false, 1) if it is not accessed any more 00907 (false, 2) if it is extracted next 00908 (false, 3) if it is initialized next */ 00909 std::deque< std::pair<bool, time_type> > next_use; 00910 00911 internal_block_type * get_free_internal_block() 00912 { 00913 // try to get a free internal_block 00914 if (internal_block_type * iblock = get_free_internal_block_from_block_scheduler()) 00915 return iblock; 00916 // evict block 00917 assert(! evictable_blocks.empty()); // fails it there is not enough memory available 00918 return swappable_blocks[evictable_blocks.pop()].detach_internal_block(); 00919 } 00920 00921 void return_free_internal_block(internal_block_type * iblock) 00922 { return_free_internal_block_to_block_scheduler(iblock); } 00923 00924 void init(block_scheduler_algorithm_type * old_algo) 00925 { 00926 std::vector< std::pair<bool, time_type> > 00927 blocks_next_acquire(swappable_blocks.size(), std::make_pair(false, 1)); 00928 if(old_algo) 00929 { 00930 // precomputations for priorities: init next_acquires 00931 const prediction_sequence_type & ps = old_algo->get_prediction_sequence(); 00932 for (typename prediction_sequence_type::const_reverse_iterator it = ps.rbegin(); it != ps.rend(); ++it) 00933 switch (it->op) 00934 { 00935 case (block_scheduler_type::op_acquire): 00936 case (block_scheduler_type::op_acquire_uninitialized): 00937 blocks_next_acquire[it->id] = std::make_pair(true, it->time); 00938 break; 00939 case (block_scheduler_type::op_release): 00940 case (block_scheduler_type::op_release_dirty): 00941 next_use.push_front(blocks_next_acquire[it->id]); 00942 break; 00943 case (block_scheduler_type::op_deinitialize): 00944 blocks_next_acquire[it->id] = std::make_pair(false, 0); 00945 break; 00946 case (block_scheduler_type::op_initialize): 00947 blocks_next_acquire[it->id] = std::make_pair(false, 3); 00948 break; 00949 case (block_scheduler_type::op_extract_external_block): 00950 blocks_next_acquire[it->id] = std::make_pair(false, 2); 00951 break; 00952 } 00953 } 00954 if (get_algorithm_from_block_scheduler()) 00955 { 00956 while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) 00957 { 00958 // insert already evictable blocks with the right priority 00959 const swappable_block_identifier_type sbid = get_algorithm_from_block_scheduler()->evictable_blocks_pop(); 00960 evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], blocks_next_acquire[sbid])); 00961 } 00962 } 00963 } 00964 00965 public: 00966 block_scheduler_algorithm_offline_lfd(block_scheduler_type & bs) 00967 : block_scheduler_algorithm_type(bs) 00968 { init(get_algorithm_from_block_scheduler()); } 00969 00970 // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence 00971 block_scheduler_algorithm_offline_lfd(block_scheduler_algorithm_type * old) 00972 : block_scheduler_algorithm_type(old) 00973 { init(old); } 00974 00975 virtual ~block_scheduler_algorithm_offline_lfd() 00976 { 00977 if (! evictable_blocks.empty()) 00978 STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lfd that still holds evictable blocks. They get deinitialized."); 00979 while (! evictable_blocks.empty()) 00980 { 00981 SwappableBlockType & sblock = swappable_blocks[evictable_blocks.pop()]; 00982 if (internal_block_type * iblock = sblock.deinitialize()) 00983 return_free_internal_block(iblock); 00984 } 00985 } 00986 00987 virtual bool evictable_blocks_empty() 00988 { return evictable_blocks.empty(); } 00989 00990 virtual swappable_block_identifier_type evictable_blocks_pop() 00991 { return evictable_blocks.pop(); } 00992 00993 virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) 00994 { 00995 SwappableBlockType & sblock = swappable_blocks[sbid]; 00996 /* acquired => internal -> increase reference count 00997 internal but not acquired -> remove from evictable_blocks, increase reference count 00998 not intern => uninitialized or external -> get internal_block, increase reference count 00999 uninitialized -> fill with default value 01000 external -> read */ 01001 if (sblock.is_internal()) 01002 { 01003 if (! sblock.is_acquired()) 01004 // not acquired yet -> remove from evictable_blocks 01005 evictable_blocks.erase(sbid); 01006 sblock.acquire(); 01007 } 01008 else if (sblock.is_initialized()) 01009 { 01010 // => external but not internal 01011 //get internal_block 01012 sblock.attach_internal_block(get_free_internal_block()); 01013 if (! uninitialized) 01014 //load block synchronously 01015 sblock.read_sync(); 01016 sblock.acquire(); 01017 } 01018 else 01019 { 01020 // => ! sblock.is_initialized() 01021 //get internal_block 01022 sblock.attach_internal_block(get_free_internal_block()); 01023 sblock.acquire(); 01024 //initialize new block 01025 if (! uninitialized) 01026 sblock.fill_default(); 01027 } 01028 return sblock.get_internal_block(); 01029 } 01030 01031 virtual void release(swappable_block_identifier_type sbid, const bool dirty) 01032 { 01033 if (next_use.empty()) 01034 { 01035 STXXL_ERRMSG("block_scheduler_algorithm_offline_lfd got release-request but prediction sequence ended. Switching to block_scheduler_algorithm_online."); 01036 // switch algorithm 01037 block_scheduler_algorithm_type * new_algo, 01038 * old_algo; 01039 new_algo = new block_scheduler_algorithm_online_lru<SwappableBlockType>(bs); 01040 old_algo = bs.switch_algorithm_to(new_algo); 01041 // redirect call 01042 new_algo->release(sbid, dirty); 01043 // delete self 01044 delete old_algo; 01045 return; 01046 } 01047 SwappableBlockType & sblock = swappable_blocks[sbid]; 01048 sblock.make_dirty_if(dirty); 01049 sblock.release(); 01050 if (! sblock.is_acquired()) 01051 { 01052 if (sblock.is_dirty() || sblock.is_external()) 01053 // => evictable, put in pq 01054 evictable_blocks.insert(sbid, priority(swappable_blocks[sbid], next_use.front())); 01055 else 01056 // => uninitialized, release internal block and put it in freelist 01057 return_free_internal_block(sblock.detach_internal_block()); 01058 } 01059 next_use.pop_front(); 01060 } 01061 01062 virtual void deinitialize(swappable_block_identifier_type sbid) 01063 { 01064 SwappableBlockType & sblock = swappable_blocks[sbid]; 01065 if (sblock.is_evictable()) 01066 evictable_blocks.erase(sbid); 01067 if (internal_block_type * iblock = sblock.deinitialize()) 01068 return_free_internal_block(iblock); 01069 } 01070 01071 virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) 01072 { 01073 SwappableBlockType & sblock = swappable_blocks[sbid]; 01074 sblock.initialize(eblock); 01075 } 01076 01077 virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) 01078 { 01079 SwappableBlockType & sblock = swappable_blocks[sbid]; 01080 if (sblock.is_evictable()) 01081 evictable_blocks.erase(sbid); 01082 if (sblock.is_internal()) 01083 return_free_internal_block(sblock.detach_internal_block()); 01084 return sblock.extract_external_block(); 01085 } 01086 }; 01087 01088 //! \brief Block scheduling algorithm caching via the least recently used policy (offline), 01089 //! and prefetching in addition. 01090 template <class SwappableBlockType> 01091 class block_scheduler_algorithm_offline_lru_prefetching : public block_scheduler_algorithm<SwappableBlockType> 01092 { 01093 protected: 01094 struct scheduled_block_meta; 01095 struct write_read_request; 01096 01097 typedef block_scheduler<SwappableBlockType> block_scheduler_type; 01098 typedef block_scheduler_algorithm<SwappableBlockType> block_scheduler_algorithm_type; 01099 typedef typename block_scheduler_type::internal_block_type internal_block_type; 01100 typedef typename block_scheduler_type::external_block_type external_block_type; 01101 typedef typename block_scheduler_type::swappable_block_identifier_type swappable_block_identifier_type; 01102 typedef typename block_scheduler_algorithm_type::time_type time_type; 01103 typedef typename block_scheduler_type::prediction_sequence_type prediction_sequence_type; 01104 typedef typename block_scheduler_type::block_scheduler_operation block_scheduler_operation; 01105 typedef typename std::vector<SwappableBlockType>::iterator swappable_blocks_iterator; 01106 01107 typedef std::map<swappable_block_identifier_type, scheduled_block_meta> scheduled_blocks_type; 01108 typedef typename scheduled_blocks_type::iterator scheduled_blocks_iterator; 01109 typedef typename scheduled_blocks_type::reference scheduled_blocks_reference; 01110 typedef std::map<swappable_block_identifier_type, write_read_request *> write_scheduled_blocks_type; 01111 typedef typename write_scheduled_blocks_type::iterator write_scheduled_blocks_iterator; 01112 typedef typename write_scheduled_blocks_type::reference write_scheduled_blocks_reference; 01113 01114 using block_scheduler_algorithm_type::bs; 01115 using block_scheduler_algorithm_type::swappable_blocks; 01116 using block_scheduler_algorithm_type::get_algorithm_from_block_scheduler; 01117 using block_scheduler_algorithm_type::get_free_internal_block_from_block_scheduler; 01118 using block_scheduler_algorithm_type::return_free_internal_block_to_block_scheduler; 01119 using block_scheduler_algorithm_type::prediction_sequence; 01120 01121 struct write_read_request 01122 { 01123 bool write_done_soon; // set by read_after_write, checked by schedule_read() 01124 bool shall_read; // checked by read_after_write, set by schedule_read() 01125 swappable_blocks_iterator block_to_start_read; // used by read_after_write, set by schedule_read() 01126 scheduled_blocks_iterator taker; // read_req set by read_after_write 01127 request_ptr write_req; // completes with read_after_write 01128 01129 write_read_request() 01130 : write_done_soon(false), shall_read(false), block_to_start_read(), taker(), write_req(0) {} 01131 }; 01132 01133 struct read_after_write 01134 { 01135 write_read_request * wrr; 01136 01137 read_after_write(write_read_request * write_read_req) 01138 : wrr(write_read_req) {} 01139 01140 void operator () (request *) 01141 { 01142 wrr->write_done_soon = true; 01143 if (wrr->shall_read) 01144 wrr->taker->second.read_req = wrr->block_to_start_read->read_async(); 01145 } 01146 }; 01147 01148 struct scheduled_block_meta 01149 { 01150 internal_block_type * reserved_iblock; 01151 std::pair<bool, swappable_block_identifier_type> giver; 01152 request_ptr read_req; 01153 std::deque<block_scheduler_operation> operations; // invariant: not empty; front: last scheduled operation, back: upcoming operation 01154 01155 scheduled_block_meta(block_scheduler_operation op) 01156 : reserved_iblock(0), 01157 giver(false, 0), 01158 read_req(0), 01159 operations() 01160 { operations.push_front(op); } 01161 }; 01162 01163 //! \brief Holds swappable blocks, whose internal block can be freed, i.e. that are internal but unacquired. 01164 std::set<swappable_block_identifier_type> free_evictable_blocks; 01165 std::set<swappable_block_identifier_type> scheduled_evictable_blocks; 01166 //! \brief Holds not internal swappable_blocks, whose next access has already been scheduled. 01167 scheduled_blocks_type scheduled_blocks; 01168 //! \brief Holds swappable_blocks, whose internal block has been taken away but the clean did not finish yet. 01169 write_scheduled_blocks_type write_scheduled_blocks; 01170 typename prediction_sequence_type::iterator next_op_to_schedule; 01171 01172 //! \brief Schedule an internal, possibly dirty swappable_block to write. 01173 //! 01174 //! The block becomes not dirty. if it was dirty, an entry in write_scheduled_blocks is made referencing the write_read_request. 01175 //! \param sbid block to write 01176 //! \return pointer to the write_read_request 01177 write_read_request * schedule_write(const swappable_block_identifier_type sbid) 01178 { 01179 SwappableBlockType & sblock = swappable_blocks[sbid]; 01180 write_read_request * wrr = new write_read_request; 01181 wrr->write_req = sblock.clean_async(read_after_write(wrr)); 01182 if (wrr->write_req.valid()) 01183 { 01184 bool t = write_scheduled_blocks.insert(std::make_pair(sbid, wrr)).second; 01185 assert(t); 01186 return wrr; 01187 } 01188 else 01189 { 01190 delete wrr; 01191 return 0; 01192 } 01193 } 01194 01195 //! \brief try to interrupt a read scheduled in a write_read_request 01196 //! 01197 //! side-effect: possibly erases entry from write_scheduled_blocks, so the iterator writing_block may become invalid 01198 //! \return if successful 01199 bool try_interrupt_read(const write_scheduled_blocks_iterator & writing_block) 01200 { 01201 // stop read 01202 writing_block->second->shall_read = false; 01203 // check if stopped 01204 if (! writing_block->second->write_done_soon) 01205 return true; 01206 // => possibly to late 01207 scheduled_blocks_reference taker = *writing_block->second->taker; 01208 // wait 01209 wait_on_write(writing_block); 01210 // check if read started 01211 if (taker.second.read_req.valid()) 01212 // => read started, to late 01213 return false; 01214 else 01215 // => just in time 01216 return true; 01217 } 01218 01219 //! \brief Schedule an internal and external block to read. 01220 //! 01221 //! If the giver is still writing, schedule read via its write_read_request. 01222 void schedule_read(scheduled_blocks_iterator block_to_read) 01223 { 01224 // first check if block_to_read is still writing. do not read before write finished 01225 // wait_on_write(block_to_read->first); 01226 01227 write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->first); 01228 if (it != write_scheduled_blocks.end()) 01229 { 01230 scheduled_blocks_iterator other_block_to_read = it->second->taker; 01231 // check if scheduled to read 01232 if (it->second->shall_read) 01233 { 01234 if (try_interrupt_read(it)) 01235 { 01236 // => interrupted, swap internal_blocks 01237 std::swap(other_block_to_read->second.giver, block_to_read->second.giver); 01238 if (other_block_to_read->second.giver.first) 01239 { 01240 write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second); 01241 if (it != write_scheduled_blocks.end()) 01242 it->second->taker = other_block_to_read; 01243 else 01244 other_block_to_read->second.giver.first = false; 01245 } 01246 if (block_to_read->second.giver.first) 01247 { 01248 write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second); 01249 if (it != write_scheduled_blocks.end()) 01250 it->second->taker = block_to_read; 01251 else 01252 block_to_read->second.giver.first = false; 01253 } 01254 01255 internal_block_type * tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block(); 01256 swappable_blocks[block_to_read->first].attach_internal_block( 01257 swappable_blocks[other_block_to_read->first].detach_internal_block()); 01258 swappable_blocks[other_block_to_read->first].attach_internal_block(tmp_iblock); 01259 // => this block has its internal_block back, no need to read 01260 // reschedule other 01261 schedule_read(other_block_to_read); 01262 return; 01263 } 01264 // else => read already started, but write done -> read this 01265 } 01266 else 01267 { 01268 // => no read scheduled, swap internal_blocks 01269 std::swap(other_block_to_read->second.giver, block_to_read->second.giver); 01270 if (other_block_to_read->second.giver.first) 01271 { 01272 write_scheduled_blocks_iterator it = write_scheduled_blocks.find(other_block_to_read->second.giver.second); 01273 if (it != write_scheduled_blocks.end()) 01274 it->second->taker = other_block_to_read; 01275 else 01276 other_block_to_read->second.giver.first = false; 01277 } 01278 if (block_to_read->second.giver.first) 01279 { 01280 write_scheduled_blocks_iterator it = write_scheduled_blocks.find(block_to_read->second.giver.second); 01281 if (it != write_scheduled_blocks.end()) 01282 it->second->taker = block_to_read; 01283 else 01284 block_to_read->second.giver.first = false; 01285 } 01286 01287 internal_block_type * tmp_iblock = swappable_blocks[block_to_read->first].detach_internal_block(); 01288 swappable_blocks[block_to_read->first].attach_internal_block( 01289 other_block_to_read->second.reserved_iblock); 01290 other_block_to_read->second.reserved_iblock = tmp_iblock; 01291 // => this block has its internal_block back, no need to read 01292 return; 01293 } 01294 } 01295 01296 // schedule block_to_read to read 01297 if (block_to_read->second.giver.first) 01298 { 01299 write_scheduled_blocks_iterator writing_block = write_scheduled_blocks.find(block_to_read->second.giver.second); 01300 if (writing_block != write_scheduled_blocks.end()) 01301 { 01302 // => there is a write scheduled 01303 // tell the completion handler that we want a read 01304 writing_block->second->block_to_start_read = swappable_blocks.begin() + block_to_read->first; 01305 writing_block->second->taker = block_to_read; 01306 writing_block->second->shall_read = true; 01307 // and check if it is not to late 01308 if (writing_block->second->write_done_soon) 01309 { 01310 // => the completion handler may have missed our wish to read 01311 // so wait for it to finish and check 01312 wait_on_write(writing_block); 01313 block_to_read->second.giver.first = false; 01314 if (block_to_read->second.read_req.valid()) 01315 // read scheduled 01316 return; 01317 } 01318 else 01319 // read scheduled 01320 return; 01321 } 01322 else 01323 block_to_read->second.giver.first = false; 01324 } 01325 // => read could not be scheduled through the completion handler 01326 block_to_read->second.read_req = swappable_blocks[block_to_read->first].read_async(); 01327 } 01328 01329 //! \brief wait for the write to finish 01330 //! 01331 //! side-effect: erases entry from write_scheduled_blocks 01332 void wait_on_write(const write_scheduled_blocks_iterator & writing_block) 01333 { 01334 writing_block->second->write_req->wait(); 01335 delete writing_block->second; 01336 write_scheduled_blocks.erase(writing_block); 01337 } 01338 01339 //! \brief wait for the write to finish 01340 //! 01341 //! side-effect: erases entry from write_scheduled_blocks 01342 void wait_on_write(const swappable_block_identifier_type & writing_block) 01343 { 01344 write_scheduled_blocks_iterator it = write_scheduled_blocks.find(writing_block); 01345 if (it != write_scheduled_blocks.end()) 01346 wait_on_write(it); 01347 } 01348 01349 //! \brief wait for the write of the giver to finish 01350 //! 01351 //! side-effect: erases entry from write_scheduled_blocks 01352 void wait_on_write(const scheduled_blocks_iterator & schedule_meta) 01353 { 01354 if (schedule_meta->second.giver.first) 01355 { 01356 wait_on_write(schedule_meta->second.giver.second); 01357 schedule_meta->second.giver.first = false; 01358 } 01359 } 01360 01361 //! \brief wait for the read to finish 01362 //! 01363 //! side-effect: erases entry for the write of the giver from write_scheduled_blocks 01364 void wait_on_read(const scheduled_blocks_iterator & schedule_meta) 01365 { 01366 wait_on_write(schedule_meta); 01367 if (schedule_meta->second.read_req.valid()) 01368 { 01369 schedule_meta->second.read_req->wait(); 01370 schedule_meta->second.read_req = 0; 01371 } 01372 } 01373 01374 //! \brief wait for the write of the giver to finish and return reserved internal_block 01375 //! 01376 //! side-effect: erases entry for the write of the giver from write_scheduled_blocks 01377 internal_block_type * get_ready_block(const scheduled_blocks_iterator & schedule_meta) 01378 { 01379 wait_on_write(schedule_meta); 01380 internal_block_type * r = schedule_meta->second.reserved_iblock; 01381 schedule_meta->second.reserved_iblock = 0; 01382 return r; 01383 } 01384 01385 bool shall_keep_internal_block(const scheduled_blocks_iterator & schedule_meta, const bool ignore_first = true) const 01386 { 01387 // returns true iif there is an acquire or acquire_uninitialized scheduled or there is a deinitialize scheduled and the block is dirty 01388 for (typename std::deque<block_scheduler_operation>::reverse_iterator 01389 rit = schedule_meta->second.operations.rbegin() + ignore_first; 01390 rit != schedule_meta->second.operations.rend(); ++rit) 01391 switch (*rit) 01392 { 01393 case block_scheduler_type::op_acquire: 01394 case block_scheduler_type::op_acquire_uninitialized: 01395 return true; break; 01396 case block_scheduler_type::op_release: 01397 case block_scheduler_type::op_release_dirty: 01398 break; 01399 case block_scheduler_type::op_deinitialize: 01400 if (swappable_blocks[schedule_meta->first].is_dirty()) return true; break; 01401 case block_scheduler_type::op_initialize: 01402 case block_scheduler_type::op_extract_external_block: 01403 break; 01404 } 01405 return false; 01406 } 01407 01408 // assumes the current operation to be still in operations 01409 bool shall_be_cleaned(const scheduled_blocks_iterator & schedule_meta) const 01410 { 01411 // returns true iif there is an extract_external_block scheduled and no release_dirty, deinitialize or initialize before 01412 for (typename std::deque<block_scheduler_operation>::reverse_iterator 01413 rit = schedule_meta->second.operations.rbegin() + 1; 01414 rit != schedule_meta->second.operations.rend(); ++rit) 01415 switch (*rit) 01416 { 01417 case block_scheduler_type::op_acquire: 01418 case block_scheduler_type::op_acquire_uninitialized: 01419 case block_scheduler_type::op_release: 01420 break; 01421 case block_scheduler_type::op_release_dirty: 01422 case block_scheduler_type::op_deinitialize: 01423 case block_scheduler_type::op_initialize: 01424 return false; break; 01425 case block_scheduler_type::op_extract_external_block: 01426 return true; break; 01427 } 01428 return false; 01429 } 01430 01431 bool shall_be_read(const scheduled_blocks_iterator & schedule_meta, const bool ignore_first = true) const 01432 { 01433 // returns true iif there is an acquire scheduled next and the block is initialized 01434 return swappable_blocks[schedule_meta->first].is_initialized() 01435 && schedule_meta->second.operations.rbegin() + ignore_first != schedule_meta->second.operations.rend() 01436 && *(schedule_meta->second.operations.rbegin() + ignore_first) == block_scheduler_type::op_acquire; 01437 } 01438 01439 void operation_done(scheduled_blocks_iterator & schedule_meta) 01440 { 01441 schedule_meta->second.operations.pop_back(); 01442 if (schedule_meta->second.operations.empty()) 01443 { 01444 assert(! schedule_meta->second.giver.first); 01445 scheduled_blocks.erase(schedule_meta); 01446 } 01447 } 01448 01449 block_scheduler_algorithm_type * give_up(std::string err_msg = "detected some error in the prediction sequence") 01450 { 01451 STXXL_ERRMSG("block_scheduler_algorithm_offline_lru_prefetching: " << err_msg << ". Switching to block_scheduler_algorithm_online."); 01452 // switch algorithm 01453 block_scheduler_algorithm_type * new_algo 01454 = new block_scheduler_algorithm_online_lru<SwappableBlockType>(bs); 01455 // and delete self 01456 delete bs.switch_algorithm_to(new_algo); 01457 return new_algo; 01458 } 01459 01460 void return_free_internal_block(internal_block_type * iblock) 01461 { return_free_internal_block_to_block_scheduler(iblock); } 01462 01463 void schedule_next_operations() 01464 { 01465 while (next_op_to_schedule != prediction_sequence.end()) 01466 { 01467 // list operation in scheduled_blocks 01468 std::pair<scheduled_blocks_iterator, bool> ins_res = scheduled_blocks.insert( 01469 std::make_pair(next_op_to_schedule->id, next_op_to_schedule->op)); 01470 scheduled_blocks_iterator schedule_meta = ins_res.first; 01471 if (! ins_res.second) 01472 schedule_meta->second.operations.push_front(next_op_to_schedule->op); 01473 SwappableBlockType & sblock = swappable_blocks[next_op_to_schedule->id]; 01474 01475 // do appropriate preparations 01476 if (next_op_to_schedule->op == block_scheduler_type::op_acquire 01477 || next_op_to_schedule->op == block_scheduler_type::op_acquire_uninitialized) 01478 { 01479 if (sblock.is_internal()) 01480 { 01481 if (free_evictable_blocks.erase(next_op_to_schedule->id)) 01482 scheduled_evictable_blocks.insert(next_op_to_schedule->id); 01483 } 01484 else 01485 if (! schedule_meta->second.reserved_iblock) 01486 { 01487 // => needs internal_block -> try to get one 01488 // -> try to get one from block_scheduler 01489 if (! (schedule_meta->second.reserved_iblock = get_free_internal_block_from_block_scheduler())) 01490 { 01491 // -> try to get one by evicting 01492 if (free_evictable_blocks.empty()) 01493 { 01494 // => can not schedule acquire 01495 // remove operation from scheduled_blocks 01496 if (ins_res.second) 01497 scheduled_blocks.erase(ins_res.first); 01498 else 01499 schedule_meta->second.operations.pop_front(); 01500 // stop scheduling 01501 return; 01502 } 01503 swappable_block_identifier_type giver = pop_begin(free_evictable_blocks); 01504 { 01505 scheduled_blocks_iterator giver_meta; 01506 assert((giver_meta = scheduled_blocks.find(giver)) == scheduled_blocks.end() 01507 || ! shall_keep_internal_block(giver_meta, false)); 01508 } 01509 write_read_request * wrr = schedule_write(giver); 01510 schedule_meta->second.giver.first = bool(wrr); 01511 schedule_meta->second.giver.second = giver; 01512 schedule_meta->second.reserved_iblock = swappable_blocks[giver].detach_internal_block(); 01513 if (wrr) 01514 wrr->taker = schedule_meta; 01515 } 01516 // read if desired 01517 if (shall_be_read(schedule_meta, false)) 01518 { 01519 // => there is no operation scheduled for this block before this acquire and it is initialized 01520 // -> start prefetching now 01521 sblock.attach_internal_block(schedule_meta->second.reserved_iblock); 01522 schedule_meta->second.reserved_iblock = 0; 01523 scheduled_evictable_blocks.insert(next_op_to_schedule->id); 01524 schedule_read(schedule_meta); 01525 } 01526 } 01527 } 01528 else if (next_op_to_schedule->op == block_scheduler_type::op_deinitialize) 01529 { 01530 if (sblock.is_dirty()) 01531 if (free_evictable_blocks.erase(next_op_to_schedule->id)) 01532 scheduled_evictable_blocks.insert(next_op_to_schedule->id); 01533 } 01534 01535 ++ next_op_to_schedule; 01536 } 01537 for (typename std::set<swappable_block_identifier_type>::iterator it = free_evictable_blocks.begin(); 01538 it != free_evictable_blocks.end(); ++it) 01539 if (! write_scheduled_blocks.count(*it)) 01540 schedule_write(*it); 01541 } 01542 01543 void init(block_scheduler_algorithm_type * old_algo) 01544 { 01545 if(old_algo) 01546 // copy prediction sequence 01547 prediction_sequence = old_algo->get_prediction_sequence(); 01548 next_op_to_schedule = prediction_sequence.begin(); 01549 if (get_algorithm_from_block_scheduler()) 01550 while (! get_algorithm_from_block_scheduler()->evictable_blocks_empty()) 01551 free_evictable_blocks.insert(get_algorithm_from_block_scheduler()->evictable_blocks_pop()); 01552 schedule_next_operations(); 01553 } 01554 01555 void deinit() 01556 { 01557 // todo remove 01558 if (! scheduled_blocks.empty()) 01559 STXXL_MSG("deinit while scheduled_blocks not empty"); 01560 if (! scheduled_evictable_blocks.empty()) 01561 STXXL_MSG("deinit while scheduled_evictable_blocks not empty"); 01562 01563 // empty scheduled_blocks 01564 free_evictable_blocks.insert(scheduled_evictable_blocks.begin(), scheduled_evictable_blocks.end()); 01565 //for (typename std::set<swappable_block_identifier_type>::iterator it = scheduled_evictable_blocks.begin(); 01566 // it != scheduled_evictable_blocks.end(); ++it) 01567 // free_evictable_blocks.insert(*it); 01568 scheduled_evictable_blocks.clear(); 01569 while (! scheduled_blocks.empty()) 01570 { 01571 scheduled_blocks_iterator it = scheduled_blocks.begin(); 01572 wait_on_read(it); 01573 if (it->second.reserved_iblock) 01574 return_free_internal_block(it->second.reserved_iblock); 01575 scheduled_blocks.erase(it); 01576 } 01577 while (! write_scheduled_blocks.empty()) 01578 { 01579 write_scheduled_blocks_iterator it = write_scheduled_blocks.begin(); 01580 wait_on_write(it); 01581 } 01582 } 01583 01584 public: 01585 block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_type & bs) 01586 : block_scheduler_algorithm_type(bs) 01587 { init(get_algorithm_from_block_scheduler()); } 01588 01589 // It is possible to keep an old simulation-algorithm object and reuse it's prediction sequence 01590 block_scheduler_algorithm_offline_lru_prefetching(block_scheduler_algorithm_type * old) 01591 : block_scheduler_algorithm_type(old) 01592 { init(old); } 01593 01594 virtual ~block_scheduler_algorithm_offline_lru_prefetching() 01595 { 01596 deinit(); 01597 if (! free_evictable_blocks.empty()) 01598 STXXL_ERRMSG("Destructing block_scheduler_algorithm_offline_lru_prefetching that still holds evictable blocks. They get deinitialized."); 01599 while (! free_evictable_blocks.empty()) 01600 { 01601 SwappableBlockType & sblock = swappable_blocks[pop_begin(free_evictable_blocks)]; 01602 if (internal_block_type * iblock = sblock.deinitialize()) 01603 return_free_internal_block(iblock); 01604 } 01605 } 01606 01607 virtual bool evictable_blocks_empty() 01608 { 01609 deinit(); 01610 return free_evictable_blocks.empty(); 01611 } 01612 01613 virtual swappable_block_identifier_type evictable_blocks_pop() 01614 { return pop_begin(free_evictable_blocks); } 01615 01616 virtual internal_block_type & acquire(const swappable_block_identifier_type sbid, const bool uninitialized = false) 01617 { 01618 assert(! prediction_sequence.empty()); 01619 assert(prediction_sequence.front().op == 01620 ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire)); 01621 assert(prediction_sequence.front().id == sbid); 01622 prediction_sequence.pop_front(); 01623 scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); 01624 assert(schedule_meta != scheduled_blocks.end()); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory) 01625 assert(schedule_meta->second.operations.back() == 01626 ((uninitialized) ? block_scheduler_type::op_acquire_uninitialized : block_scheduler_type::op_acquire)); // acquire not scheduled or out of internal_blocks (i.e. not enough internal memory) 01627 01628 SwappableBlockType & sblock = swappable_blocks[sbid]; 01629 /* acquired => internal -> increase reference count 01630 internal but not acquired -> remove from scheduled_evictable_blocks, increase reference count 01631 not internal => uninitialized or external -> get internal_block, increase reference count 01632 uninitialized -> fill with default value 01633 external -> read */ 01634 if (sblock.is_internal()) 01635 { 01636 if (! sblock.is_acquired()) 01637 { 01638 // not acquired yet -> remove from scheduled_evictable_blocks 01639 bool t = scheduled_evictable_blocks.erase(sbid); 01640 assert(t); 01641 wait_on_read(schedule_meta); 01642 } 01643 sblock.acquire(); 01644 } 01645 else 01646 { 01647 assert(uninitialized || ! sblock.is_initialized()); // initialized blocks should be scheduled to read and thus internal 01648 //get internal_block 01649 sblock.attach_internal_block(get_ready_block(schedule_meta)); 01650 sblock.acquire(); 01651 //initialize new block 01652 if (! uninitialized) 01653 sblock.fill_default(); 01654 } 01655 01656 operation_done(schedule_meta); 01657 return sblock.get_internal_block(); 01658 } 01659 01660 virtual void release(swappable_block_identifier_type sbid, const bool dirty) 01661 { 01662 assert(! prediction_sequence.empty()); 01663 assert(prediction_sequence.front().op == 01664 ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release)); 01665 assert(prediction_sequence.front().id == sbid); 01666 prediction_sequence.pop_front(); 01667 scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); 01668 assert(schedule_meta != scheduled_blocks.end()); 01669 assert(schedule_meta->second.operations.back() == 01670 ((dirty) ? block_scheduler_type::op_release_dirty : block_scheduler_type::op_release)); 01671 01672 SwappableBlockType & sblock = swappable_blocks[sbid]; 01673 sblock.make_dirty_if(dirty); 01674 sblock.release(); 01675 if (! sblock.is_acquired()) 01676 { 01677 if (sblock.is_dirty() || sblock.is_external()) 01678 { 01679 // => evictable 01680 if (shall_keep_internal_block(schedule_meta)) 01681 { 01682 // => swappable_block shall keep its internal_block 01683 scheduled_evictable_blocks.insert(sbid); 01684 if (shall_be_cleaned(schedule_meta)) 01685 schedule_write(sbid); 01686 } 01687 else 01688 { 01689 // give block to scheduler 01690 free_evictable_blocks.insert(sbid); 01691 if (next_op_to_schedule != prediction_sequence.end()) 01692 schedule_next_operations(); 01693 else 01694 if (! write_scheduled_blocks.count(sbid)) 01695 schedule_write(sbid); 01696 } 01697 } 01698 else 01699 { 01700 // => uninitialized 01701 if (shall_keep_internal_block(schedule_meta)) 01702 // => swappable_block shall keep its internal_block 01703 schedule_meta->second.reserved_iblock = sblock.detach_internal_block(); 01704 else 01705 { 01706 // release internal block and give it to prefetcher 01707 return_free_internal_block(sblock.detach_internal_block()); 01708 if (next_op_to_schedule != prediction_sequence.end()) 01709 schedule_next_operations(); 01710 } 01711 } 01712 } 01713 operation_done(schedule_meta); 01714 } 01715 01716 virtual void deinitialize(swappable_block_identifier_type sbid) 01717 { 01718 assert(! prediction_sequence.empty()); 01719 assert(prediction_sequence.front().op == block_scheduler_type::op_deinitialize); 01720 assert(prediction_sequence.front().id == sbid); 01721 prediction_sequence.pop_front(); 01722 scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); 01723 assert(schedule_meta != scheduled_blocks.end()); 01724 assert(schedule_meta->second.operations.back() == block_scheduler_type::op_deinitialize); 01725 01726 SwappableBlockType & sblock = swappable_blocks[sbid]; 01727 if (sblock.is_evictable()) 01728 { 01729 bool t; 01730 if (shall_keep_internal_block(schedule_meta, false)) 01731 { 01732 if (! (t = scheduled_evictable_blocks.erase(sbid))) 01733 { 01734 STXXL_ERRMSG("dirty block not scheduled on deinitialize"); 01735 t = free_evictable_blocks.erase(sbid); 01736 } 01737 } 01738 else 01739 t = free_evictable_blocks.erase(sbid); 01740 assert(t); 01741 } 01742 if (internal_block_type * iblock = sblock.deinitialize()) 01743 { 01744 if (shall_keep_internal_block(schedule_meta)) 01745 // => swappable_block shall keep its internal_block 01746 schedule_meta->second.reserved_iblock = iblock; 01747 else 01748 { 01749 // release internal block and give it to prefetcher 01750 return_free_internal_block(iblock); 01751 if (next_op_to_schedule != prediction_sequence.end()) 01752 schedule_next_operations(); 01753 } 01754 } 01755 operation_done(schedule_meta); 01756 } 01757 01758 virtual void initialize(swappable_block_identifier_type sbid, external_block_type eblock) 01759 { 01760 assert(! prediction_sequence.empty()); 01761 assert(prediction_sequence.front().op == block_scheduler_type::op_initialize); 01762 assert(prediction_sequence.front().id == sbid); 01763 prediction_sequence.pop_front(); 01764 scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); 01765 assert(schedule_meta != scheduled_blocks.end()); 01766 assert(schedule_meta->second.operations.back() == block_scheduler_type::op_initialize); 01767 01768 SwappableBlockType & sblock = swappable_blocks[sbid]; 01769 sblock.initialize(eblock); 01770 if (shall_be_read(schedule_meta)) 01771 { 01772 sblock.attach_internal_block(schedule_meta->second.reserved_iblock); 01773 schedule_meta->second.reserved_iblock = 0; 01774 scheduled_evictable_blocks.insert(sbid); 01775 schedule_read(schedule_meta); 01776 } 01777 operation_done(schedule_meta); 01778 } 01779 01780 virtual external_block_type extract_external_block(swappable_block_identifier_type sbid) 01781 { 01782 assert(! prediction_sequence.empty()); 01783 assert(prediction_sequence.front().op == block_scheduler_type::op_extract_external_block); 01784 assert(prediction_sequence.front().id == sbid); 01785 prediction_sequence.pop_front(); 01786 scheduled_blocks_iterator schedule_meta = scheduled_blocks.find(sbid); 01787 assert(schedule_meta != scheduled_blocks.end()); 01788 assert(schedule_meta->second.operations.back() == block_scheduler_type::op_extract_external_block); 01789 01790 SwappableBlockType & sblock = swappable_blocks[sbid]; 01791 wait_on_write(sbid); 01792 operation_done(schedule_meta); 01793 return sblock.extract_external_block(); 01794 } 01795 }; 01796 01797 __STXXL_END_NAMESPACE 01798 01799 #endif /* STXXL_BLOCK_SCHEDULER_HEADER */