/* * dwf.cc * * Copyright (c) 2009 by Tor M. Aamodt, Wilson W. L. Fung, and the * University of British Columbia * Vancouver, BC V6T 1Z4 * All Rights Reserved. * * THIS IS A LEGAL DOCUMENT BY DOWNLOADING GPGPU-SIM, YOU ARE AGREEING TO THESE * TERMS AND CONDITIONS. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * NOTE: The files libcuda/cuda_runtime_api.c and src/cuda-sim/cuda-math.h * are derived from the CUDA Toolset available from http://www.nvidia.com/cuda * (property of NVIDIA). The files benchmarks/BlackScholes/ and * benchmarks/template/ are derived from the CUDA SDK available from * http://www.nvidia.com/cuda (also property of NVIDIA). The files from * src/intersim/ are derived from Booksim (a simulator provided with the * textbook "Principles and Practices of Interconnection Networks" available * from http://cva.stanford.edu/books/ppin/). As such, those files are bound by * the corresponding legal terms and conditions set forth separately (original * copyright notices are left in files from these sources and where we have * modified a file our copyright notice appears before the original copyright * notice). * * Using this version of GPGPU-Sim requires a complete installation of CUDA * which is distributed seperately by NVIDIA under separate terms and * conditions. To use this version of GPGPU-Sim with OpenCL requires a * recent version of NVIDIA's drivers which support OpenCL. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. Neither the name of the University of British Columbia nor the names of * its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * 4. This version of GPGPU-SIM is distributed freely for non-commercial use only. * * 5. No nonprofit user may place any restrictions on the use of this software, * including as modified by the user, by any other authorized user. * * 6. GPGPU-SIM was developed primarily by Tor M. Aamodt, Wilson W. L. Fung, * Ali Bakhoda, George L. Yuan, at the University of British Columbia, * Vancouver, BC V6T 1Z4 */ #include "dwf.h" #include "histogram.h" #include #include #include #include #include using namespace std; unsigned int gpgpu_dwf_regbk = 1; unsigned int gpgpu_dwf_heuristic = 0; enum { MAJORITY = 0, MINORITY = 1, FIFO = 2, PDOMPRIO = 3, PC = 4, MAJORITY_MAXHEAP = 5, N_DWFMODE }; typedef struct warp_entry { address_type pc; int* tid; // thread id's int occ; // occupancy vector int pdom_prio; // pdom_priority int pdom_occ; // pdom_priority's aux data int next_warp; // index to next warp in an implicit queue void* lut_ptr; // pointer to the warp lut entry that last update this warp (a hack), done to decouple warp lut and warp pool int uid; // unique id of a warp } warp_entry_t; class issue_warp_majority { public: virtual void add_threads( address_type pc, int *tid) = 0; virtual void push_warp( address_type pc, int idx) = 0; virtual int pop_warp( ) = 0; virtual void print( FILE *fout ) = 0; virtual ~issue_warp_majority( ) {} }; typedef struct maxheap_lut_entry { address_type pc; // pc of the warps int maxheap_idx; // index to the max heap } maxheap_lut_entry_t; typedef struct maxheap_entry { address_type pc; // pc of the warps int n_thds; // number of threads with this pc (from lut) int wpool_head; // the first warp with this pc int wpool_tail; // the last warp with this pc int lut_idx; // reverse index to the lut (for update in entry movement) } maxheap_entry_t; class mh_lut_class { private: maxheap_lut_entry_t *lut_data; list *lru_stack; // front = LRU int n_set; int insn_size_lgb2; public: int size; int assoc; int n_read; int n_write; int n_read_per_cycle; int n_write_per_cycle; int n_aliased; static maxheap_lut_entry_t clean_entry; mh_lut_class (int size, int assoc, int n_read_per_cycle, int n_write_per_cycle ) { int i; this->size = size; this->assoc = assoc; lut_data = new maxheap_lut_entry_t[size]; for (i=0; i[n_set]; for (i=0; in_read_per_cycle = n_read_per_cycle; this->n_write_per_cycle = n_write_per_cycle; this->n_read = 0; this->n_write = 0; this->n_aliased = 0; } ~mh_lut_class ( ) { delete[] lut_data; } // obtain entry at a known location maxheap_lut_entry_t get( int lut_idx ) { assert(lut_idx < size); n_read++; return lut_data[lut_idx]; } // modify an entry at a known location void set( int lut_idx, maxheap_lut_entry_t lut_entry ) { n_write++; lut_data[lut_idx] = lut_entry; } // update a lut entry with a new index void update_mh_idx( int lut_idx, int mh_idx ) { n_write++; lut_data[lut_idx].maxheap_idx = mh_idx; } // lookup an entry with a pc int lookup( address_type pc ) { int i; int lut_idx = -1; int set_start_idx = get_set(pc) * assoc; // look for the matched entry within the set for (i = set_start_idx; i < (set_start_idx + assoc); i++) { if (lut_data[i].pc == pc) { lut_idx = i; } } // update lru stack if hit if (lut_idx != -1) { int set_idx = set_start_idx / assoc; list::iterator it; it = find(lru_stack[set_idx].begin(), lru_stack[set_idx].end(), lut_idx); if (it != lru_stack[set_idx].end()) { lru_stack[set_idx].erase(it); lru_stack[set_idx].push_back(lut_idx); } } return lut_idx; } void free(int lut_idx) { set(lut_idx, clean_entry); int set_idx = lut_idx / assoc; list::iterator it; it = find(lru_stack[set_idx].begin(), lru_stack[set_idx].end(), lut_idx); if (it != lru_stack[set_idx].end()) { lru_stack[set_idx].erase(it); lru_stack[set_idx].push_front(lut_idx); } } // find the LRU entry to be replaced int find_lru( maxheap_lut_entry_t lut_entry ) { int set_idx = get_set(lut_entry.pc); int lru_idx = lru_stack[set_idx].front(); return lru_idx; } // actually replacing the LRU entry int replace_lru( maxheap_lut_entry_t lut_entry ) { int set_idx = get_set(lut_entry.pc); int lru_idx = lru_stack[set_idx].front(); lru_stack[set_idx].pop_front(); // counting the number of overwritten entries if (lut_data[lru_idx].maxheap_idx != 0) n_aliased++; set(lru_idx, lut_entry); lru_stack[set_idx].push_back(lru_idx); return lru_idx; } // reset the number of accesses to zero void reset_access( ) { n_read = 0; n_write = 0; } // clear the number of accesses - done at the end of scheduler cycle void clear_access( ) { n_read -= n_read_per_cycle; n_read = (n_read >= 0)? n_read : 0; n_write -= n_write_per_cycle; n_write = (n_write >= 0)? n_write : 0; } // test if the structure is done with all the required accesses int all_access_done( ) { return(n_read == 0 && n_write == 0); } void print_lut_e(FILE *fout, maxheap_lut_entry_t lut_e) { fprintf(fout, "[%08x]mh%02d", lut_e.pc, lut_e.maxheap_idx); } void print(FILE *fout) { int i, j; for (i=0; i::iterator it = lru_stack[i].begin(); for (; it != lru_stack[i].end(); it++) { fprintf(fout, "%02d-", *it); } fprintf(fout, "\n"); } } private: inline int get_set(address_type pc) { return((pc >> insn_size_lgb2) & (n_set - 1)); } }; maxheap_lut_entry_t mh_lut_class::clean_entry = {0xDEADBEEF, 0}; // A class tracking the number of accesses done to the maxheap structure // and the index ranges from 1..n_entries with 1 being the root class maxheap_class { private: maxheap_entry_t *maxheap_data; mh_lut_class *lut; public: int n_read; int n_write; int n_entries; int size; int n_read_per_cycle; int n_write_per_cycle; int max_n_entries; static maxheap_entry_t clean_entry; maxheap_class( int size, mh_lut_class *lut, int n_read_per_cycle, int n_write_per_cycle ) { n_read = 0; n_write = 0; n_entries = 0; // index to the last element this->size = size; maxheap_data = new maxheap_entry_t[size]; for (int i=0; ilut = lut; this->n_read_per_cycle = n_read_per_cycle; this->n_write_per_cycle = n_write_per_cycle; this->n_read = 0; this->n_write = 0; this->max_n_entries = 0; } ~maxheap_class( ) { delete[] maxheap_data; } // insert a new entry into the maxheap // return: the index to the new entry int insert( maxheap_entry_t mh_entry ) { assert(n_entries + 1 < size); n_write++; n_entries++; maxheap_data[n_entries] = mh_entry; max_n_entries = (max_n_entries >= n_entries)? max_n_entries : n_entries; return n_entries; } // retrieve the max heap entry at index [mh_idx] maxheap_entry_t get( int mh_idx ) { assert(mh_idx > 0); assert(mh_idx <= n_entries); n_read++; return maxheap_data[mh_idx]; } // replace the max heap entry at index [mh_idx] void set( int mh_idx, maxheap_entry_t mh_entry ) { assert(mh_idx > 0); assert(mh_idx <= n_entries); n_write++; maxheap_data[mh_idx] = mh_entry; } // a special version of set that only reset the lut_idx void remove_lut_idx( int mh_idx ) { assert(mh_idx > 0); assert(mh_idx <= n_entries); n_write++; maxheap_data[mh_idx].lut_idx = -1; } // read both childrens of a given node, count as one read // return the number of child read int get_childof(int mh_idx, maxheap_entry_t *child) { int child_idx = childof(mh_idx); int child_read = 0; if (child_idx <= n_entries) { n_read++; child[0] = maxheap_data[child_idx]; child_read++; } if (child_idx + 1 <= n_entries) { child[1] = maxheap_data[child_idx + 1]; child_read++; } return child_read; } // pop the root entry of max heap maxheap_entry_t pop_root( ) { maxheap_entry_t old_root = get(1); maxheap_entry_t curr_mhe[3]; curr_mhe[0] = get(n_entries); set(1, curr_mhe[0]); if (curr_mhe[0].lut_idx >= 0) lut->update_mh_idx(curr_mhe[0].lut_idx, 1); n_entries--; int curr_node = 1; int n_child = 0; n_child = get_childof(curr_node, curr_mhe + 1); while (n_child > 0) { int max_child = 0; int i; for (i = 1; i < n_child + 1; i++) { if (cmp_mh(curr_mhe[i], curr_mhe[max_child])) { max_child = i; } } n_child = 0; if (max_child > 0) { int max_child_node = childof(curr_node) + max_child - 1; set(curr_node, curr_mhe[max_child]); set(max_child_node, curr_mhe[0]); // update the lut for this swap if (curr_mhe[max_child].lut_idx >= 0) lut->update_mh_idx(curr_mhe[max_child].lut_idx, curr_node); if (curr_mhe[0].lut_idx >= 0) lut->update_mh_idx(curr_mhe[0].lut_idx, max_child_node); // get the next child curr_node = max_child_node; n_child = get_childof(curr_node, curr_mhe + 1); } } return old_root; } // probe if the maxheap is empty int empty( ) { return(n_entries == 0); } // reset the number of accesses to zero void reset_access( ) { n_read = 0; n_write = 0; } // clear the number of accesses - done at the end of scheduler cycle void clear_access( ) { n_read -= n_read_per_cycle; n_read = (n_read >= 0)? n_read : 0; n_write -= n_write_per_cycle; n_write = (n_write >= 0)? n_write : 0; } // test if the structure is done with all the required accesses int all_access_done( ) { return(n_read == 0 && n_write == 0); } // sort the max heap again starting from start_idx // (this entry can only go up in the tree to the root) void sort_bottomup(int start_idx) { maxheap_entry_t mh_entry; maxheap_entry_t mh_parent; if (start_idx == 1) return; // no need to resort if the root is incremented int curr_idx = start_idx; int parent_idx = parentof(start_idx); int continue_sort = 1; while (curr_idx > 1 && continue_sort) { mh_entry = get(curr_idx); mh_parent = get(parent_idx); // swap the entries if it is now larger than it's parent if (cmp_mh(mh_entry, mh_parent)) { set(parent_idx, mh_entry); set(curr_idx, mh_parent); // update the lut for this swap if (mh_entry.lut_idx >= 0) lut->update_mh_idx(mh_entry.lut_idx, parent_idx); if (mh_parent.lut_idx >= 0) lut->update_mh_idx(mh_parent.lut_idx, curr_idx); // update index for next iteration curr_idx = parent_idx; parent_idx = parentof(curr_idx); } else { // swap did not happen, no need to sort anymore continue_sort = 0; } } } void print_mh_e(FILE *fout, maxheap_entry_t mh_e) { fprintf(fout, "[%08x]%03d(H%03dT%03d)p%02d | ", mh_e.pc, mh_e.n_thds, mh_e.wpool_head, mh_e.wpool_tail, mh_e.lut_idx); } void print(FILE *fout) { fprintf(fout, "MaxHeap: "); fprintf(fout, "N_entries = %d\n", n_entries); for (int i=0; i 0); return(mh_idx / 2); } static inline int childof(int mh_idx) { return(mh_idx * 2); } static inline int cmp_mh(maxheap_entry_t &a, maxheap_entry_t &b) { if (a.n_thds > b.n_thds) return 1; if (a.n_thds == b.n_thds) { if (a.pc < b.pc) return 1; } return 0; } }; maxheap_entry_t maxheap_class::clean_entry = {0, 0, -1, -1, -1}; typedef struct mh_update_struct { int n_maxheap_read; int n_maxheap_write; int n_mhlut_read; int n_mhlut_write; } mh_update; // heap implementation of majority policy class issue_warp_majority_heap : public issue_warp_majority { public: mh_lut_class mh_lut; maxheap_class maxheap; maxheap_lut_entry_t major_lut_e; maxheap_entry_t major_mh_e; vector *warp_pool; int simd_width; int n_stall_on_maxheap; queue update_queue; static pow2_histogram n_pending_updates_histo; issue_warp_majority_heap (int simd_width = 0, vector *bp = NULL, int lut_size = 32, int lut_assoc = 4, int maxheap_size = 128, int n_read_lut = 4, int n_write_lut = 4, int n_read_mh = 4, int n_write_mh = 4) : mh_lut(lut_size, lut_assoc, n_read_lut, n_write_lut), maxheap(maxheap_size, &mh_lut, n_read_mh, n_write_mh) { this->simd_width = simd_width; this->warp_pool = bp; this->major_lut_e = mh_lut_class::clean_entry; this->major_mh_e = maxheap_class::clean_entry; this->n_stall_on_maxheap = 0; } // adding more threads to a specify pc // these threads may end up in different warpes void add_threads( address_type pc, int *tid) { int i; int n_thds = 0; for (i=0; i= 0) n_thds++; } // handle special case with adding threads to current majority pc if (major_lut_e.pc == pc) { assert(major_mh_e.pc == pc); major_mh_e.n_thds += n_thds; return; } maxheap_lut_entry_t lut_e; maxheap_entry_t mh_entry; // snapshot the current maxheap read/write demand mh_update new_mh_update; new_mh_update.n_maxheap_read = maxheap.n_read; new_mh_update.n_maxheap_write = maxheap.n_write; new_mh_update.n_mhlut_read = mh_lut.n_read; new_mh_update.n_mhlut_write = mh_lut.n_write; int lut_idx = mh_lut.lookup(pc); int sort_from_idx = 0; if (lut_idx >= 0) { // obtain the entry lut_e = mh_lut.get(lut_idx); // get the maxheap entry and update its number of threads mh_entry = maxheap.get(lut_e.maxheap_idx); mh_entry.n_thds += n_thds; maxheap.set(lut_e.maxheap_idx, mh_entry); // sort from this specific entry sort_from_idx = lut_e.maxheap_idx; } else { // create a new lut entry lut_e = mh_lut_class::clean_entry; lut_e.pc = pc; // get index to the LRU lut entry in this set lut_idx = mh_lut.find_lru(lut_e); // get the replaced lut entry and remove its link with the maxheap entry maxheap_lut_entry_t lut_old = mh_lut.get(lut_idx); if (lut_old.maxheap_idx > 0) maxheap.remove_lut_idx(lut_old.maxheap_idx); // create a new maxheap entry mh_entry = maxheap_class::clean_entry; mh_entry.pc = pc; mh_entry.n_thds = n_thds; mh_entry.lut_idx = lut_idx; // push the new entry into the maxheap and lut respectively lut_e.maxheap_idx = maxheap.insert(mh_entry); mh_lut.replace_lru(lut_e); // start sorting from the bottom? sort_from_idx = lut_e.maxheap_idx; } maxheap.sort_bottomup(sort_from_idx); // record the newly generated maxheap read/write demand from this update new_mh_update.n_maxheap_read = maxheap.n_read - new_mh_update.n_maxheap_read; new_mh_update.n_maxheap_write = maxheap.n_write - new_mh_update.n_maxheap_write; new_mh_update.n_mhlut_read = mh_lut.n_read - new_mh_update.n_mhlut_read; new_mh_update.n_mhlut_write = mh_lut.n_write - new_mh_update.n_mhlut_write; update_queue.push(new_mh_update); } // call this when a new warp allocated for a specific pc void push_warp( address_type pc, int idx) { maxheap_entry_t *p_mh_e = NULL; maxheap_entry_t mh_e; maxheap_lut_entry_t lut_e = mh_lut_class::clean_entry; int lut_idx = -1; if (major_mh_e.pc == pc) { p_mh_e = &major_mh_e; } else { lut_idx = mh_lut.lookup(pc); assert(lut_idx >= 0); // if it is a miss, a new entry should have been created already lut_e = mh_lut.get(lut_idx); mh_e = maxheap.get(lut_e.maxheap_idx); p_mh_e = &mh_e; // discounting these 'gets' // because they should be combined with the 'gets' in add_threads() mh_lut.n_read--; maxheap.n_read--; } if (p_mh_e->wpool_head == -1) { p_mh_e->wpool_head = idx; p_mh_e->wpool_tail = idx; } else { (*warp_pool)[p_mh_e->wpool_tail].next_warp = idx; p_mh_e->wpool_tail = idx; } if (major_mh_e.pc == pc) { } else { maxheap.set(lut_e.maxheap_idx, mh_e); // discounting this 'set' // because it should be combined with the 'set' in add_threads() maxheap.n_write--; } } // obtain a warp index from this issue logic int pop_warp( ) { int bidx = -1; if (major_mh_e.wpool_head == -1 && !maxheap.empty()) { if (this->all_access_done( )) { // pop the majority PC from max heap major_mh_e = maxheap.pop_root(); // pop its corresponding entry from the lut as well (if it exists) if (major_mh_e.lut_idx >= 0) { major_lut_e = mh_lut.get(major_mh_e.lut_idx); mh_lut.free(major_mh_e.lut_idx); } else { major_lut_e = mh_lut_class::clean_entry; } } else { n_stall_on_maxheap += 1; bidx = -1; return bidx; } } // just pop and entry to from the virtual queue (and set the head pointer to next warp) bidx = major_mh_e.wpool_head; if (bidx >= 0) { major_mh_e.wpool_head = (*warp_pool)[major_mh_e.wpool_head].next_warp; } return bidx; } void reset_access( ) { maxheap.reset_access(); mh_lut.reset_access(); while (!update_queue.empty()) { update_queue.pop(); } } inline void consume_access( int &req_acc, int &avl_acc) { if (req_acc > avl_acc) { req_acc -= avl_acc; avl_acc = 0; } else { avl_acc -= req_acc; req_acc = 0; } } void clear_access( ) { maxheap.clear_access(); mh_lut.clear_access(); int n_maxheap_read_bw = maxheap.n_read_per_cycle; int n_maxheap_write_bw = maxheap.n_write_per_cycle; int n_mhlut_read_bw = mh_lut.n_read_per_cycle; int n_mhlut_write_bw = mh_lut.n_write_per_cycle; while ((n_maxheap_read_bw > 0 || n_maxheap_read_bw > 0 || n_mhlut_read_bw > 0 || n_mhlut_write_bw > 0) && !update_queue.empty()) { mh_update &c_update = update_queue.front(); consume_access (c_update.n_maxheap_read, n_maxheap_read_bw); consume_access (c_update.n_maxheap_write, n_maxheap_write_bw); consume_access (c_update.n_mhlut_read, n_mhlut_read_bw); consume_access (c_update.n_mhlut_write, n_mhlut_write_bw); if (c_update.n_maxheap_read == 0 && c_update.n_maxheap_write == 0 && c_update.n_mhlut_read == 0 && c_update.n_mhlut_write == 0) { update_queue.pop(); } else { break; } } n_pending_updates_histo.add2bin(update_queue.size()); } void print( FILE *fout ) { fprintf(fout, "LUT: "); mh_lut.print_lut_e(fout, major_lut_e); fprintf(fout, " \tMH: "); maxheap.print_mh_e(fout, major_mh_e); fprintf(fout, "\n"); mh_lut.print(fout); maxheap.print(fout); } static void print_stat( FILE *fout) { fprintf(fout, "n_pending_maxheap_updates = "); n_pending_updates_histo.fprint(fout); fprintf(fout, "\n"); } private: int all_access_done( ) { return(maxheap.all_access_done() && mh_lut.all_access_done()); } }; pow2_histogram issue_warp_majority_heap::n_pending_updates_histo; class warp_queue { public: int m_pc; int n_thds; int simd_width; deque idx_queue; warp_queue( address_type pc, int simd_width) { this->m_pc = pc; this->n_thds = 0; this->simd_width = simd_width; } // called right after a lut_entry is looked up void add_threads( int *tid ) { for (int i=0; i= 0) this->n_thds++; } } // called right after a warp is issued void sub_threads( int *tid ) { for (int i=0; i= 0) this->n_thds--; } } // if other warp queue should be ahead bool operator<(const warp_queue& other) const { if (n_thds == other.n_thds) { return(m_pc > other.m_pc); // smaller pc first } else { return(n_thds < other.n_thds); } } bool operator>(const warp_queue& other) const { if (n_thds == other.n_thds) { return(m_pc > other.m_pc); // smaller pc first } else { return(n_thds > other.n_thds); } } void print( FILE *fout ) { fprintf(fout, "0x%08x(%03d)=[", m_pc, n_thds); deque::iterator dit = idx_queue.begin(); for (; dit != idx_queue.end(); dit++) { fprintf(fout, "%03d ", *dit); } fprintf(fout, "]\n"); } }; bool minor_warp( const warp_queue* a, const warp_queue* b ) { return(*a<*b); } // queue implementation of majority scheduling policy class issue_warp_majority_queue : public issue_warp_majority { public: map majority_map; set warpq_set; warp_queue* maj_warp; vector *warp_pool; int simd_width; issue_warp_majority_queue(int simd_width = 0, vector *bp = NULL) { this->maj_warp = NULL; this->simd_width = simd_width; this->warp_pool = bp; } // adding more threads to a specify pc // these threads may end up in different warps void add_threads( address_type pc, int *tid) { warp_queue* bq = majority_map[pc]; if (bq == NULL) { bq = new warp_queue(pc,simd_width); warpq_set.insert(bq); majority_map[pc] = bq; } bq->add_threads(tid); } // call this when a new warp allocated for a specific pc void push_warp( address_type pc, int idx) { warp_queue* bq = majority_map[pc]; assert(bq != NULL); bool check_redundant_idx = false; if (check_redundant_idx) { deque::iterator dit = find(bq->idx_queue.begin(), bq->idx_queue.end(), idx); assert(dit == bq->idx_queue.end()); } bq->idx_queue.push_back(idx); } // obtain a warp index from this issue logic int pop_warp( ) { int bidx = -1; // find the new majority pc if it didn't exist if (maj_warp == NULL && warpq_set.size()) { maj_warp = *max_element(warpq_set.begin(), warpq_set.end(), minor_warp); } // if a majority pc indeed exist if (maj_warp) { assert(!maj_warp->idx_queue.empty()); bidx = maj_warp->idx_queue.front(); maj_warp->idx_queue.pop_front(); maj_warp->sub_threads((*warp_pool)[bidx].tid); // when the majority pc runs out of thread if (maj_warp->n_thds == 0) { // remove that warp queue warpq_set.erase(maj_warp); majority_map.erase(maj_warp->m_pc); delete maj_warp; maj_warp = NULL; } } return bidx; } void print( FILE *fout ) { fprintf(fout, "issue_warp_majority:\n"); set::iterator dit = warpq_set.begin(); for (; dit != warpq_set.end(); dit++) { fprintf(fout, " %c ", ((*dit)==maj_warp)? 'M':' '); (*dit)->print(fout); } } void check_consistency( ) { set::iterator set_it = warpq_set.begin(); for (; set_it != warpq_set.end(); set_it++) { warp_queue* bq = (*set_it); int real_nthds = 0; deque::iterator dit = bq->idx_queue.begin(); for (; dit != bq->idx_queue.end(); dit++) { int *tid = (*warp_pool)[*dit].tid; for (int i = 0; i < simd_width; i++) { real_nthds += (tid[i] >= 0)? 1 : 0; } } assert(real_nthds == bq->n_thds); } } }; // pdom priority class lesspdom_first { public: vector *warp_pool; lesspdom_first( vector *bp=NULL ) { this->warp_pool = bp; } bool operator() (const int &idx_a, const int &idx_b) const { if ((*warp_pool)[idx_a].pdom_prio != (*warp_pool)[idx_b].pdom_prio) { return((*warp_pool)[idx_a].pdom_prio < (*warp_pool)[idx_b].pdom_prio); } else { return((*warp_pool)[idx_a].occ > (*warp_pool)[idx_b].occ); } } }; class issue_warp_pdom_prio { public: vector *warp_pool; int* thd_pdom_prio; int simd_width; int n_threads; int resort_needed; list pdom_pqueue; //the queue holding all index lesspdom_first lesspdom_cmp; static set reconvgence_pt; //table holding all recvg pt issue_warp_pdom_prio (int simd_width = 0, vector *bp = NULL, int n_threads = 0) : lesspdom_cmp(bp) { this->simd_width = simd_width; this->warp_pool = bp; this->n_threads = n_threads; this->thd_pdom_prio = new int[n_threads]; memset(this->thd_pdom_prio, 0, sizeof(int)*n_threads); this->resort_needed = 0; } ~issue_warp_pdom_prio( ) { delete[] this->thd_pdom_prio; } void reinit( ) { memset(this->thd_pdom_prio, 0, sizeof(int)*n_threads); } // adding more threads to a warp void add_threads( int idx, address_type pc) { assert((*warp_pool)[idx].pc == pc); // check to see if this is a newly allocated warp bool check_pdom = false; if ((*warp_pool)[idx].pdom_prio == -1) { check_pdom = true; } // check for newly assigned threads to the warp int pdom_occ = (*warp_pool)[idx].pdom_occ; int *tid = (*warp_pool)[idx].tid; for (int i=0; i= 0 && !(pdom_occ & (1<= 0) { thd_pdom_prio[(*warp_pool)[bidx].tid[i]] = (*warp_pool)[bidx].pdom_prio; } } } return bidx; } }; set issue_warp_pdom_prio::reconvgence_pt = set(); //*/ class npc_tracker_class { public: map pc_count; unsigned* acc_pc_count; int simd_width; static map histogram; npc_tracker_class( ) { this->acc_pc_count = NULL; this->simd_width = 0; } npc_tracker_class(unsigned* acc_pc_count, int simd_width) { this->acc_pc_count = acc_pc_count; this->simd_width = simd_width; } void add_threads( int *tid, address_type pc ) { for (int i=0; i= 0); if (pc_count[pc] == 0) pc_count.erase(pc); // manually erasing entries with 0 count } } } void update_acc_count( ) { (*acc_pc_count) += pc_count.size(); histogram[pc_count.size()] += 1; } unsigned count( ) { return pc_count.size();} static void histo_print( FILE* fout ) { map::iterator i; fprintf(fout, "DYHW nPC Histogram: "); for (i = histogram.begin(); i != histogram.end(); i++) { fprintf(fout, "%d:%d ", i->first, i->second); } fprintf(fout, "\n"); } }; map npc_tracker_class::histogram; class pc_tag { private: address_type m_pc; public: pc_tag () { this->reset(); } pc_tag (const pc_tag& p) { this->m_pc = p.m_pc;} pc_tag (const address_type& other_pc) { this->m_pc = other_pc;} pc_tag& operator=(const pc_tag& p) { m_pc = p.m_pc; return *this;} pc_tag& operator=(const address_type& other_pc) { m_pc = other_pc; return *this;} inline bool operator==(const pc_tag& p) const { return(m_pc == p.m_pc);} inline bool operator==(const address_type& other_pc) const { return(m_pc == other_pc);} inline bool operator!=(const pc_tag& p) const { return(m_pc != p.m_pc);} inline bool operator!=(const address_type& other_pc) const { return(m_pc != other_pc);} inline bool operator<(const pc_tag& p) const { return(m_pc < p.m_pc);} inline void reset() { m_pc = -1; } inline address_type get_pc() const { return m_pc;} // the hash function to warp LUT inline unsigned lut_hash( int insn_size_lgb2, int lut_nsets ) const { return(m_pc >> insn_size_lgb2) & (lut_nsets - 1); } inline void to_print(char *buffer, unsigned length) { snprintf(buffer, length, "0x%08x", m_pc); } }; template class tag2warp_entry_t { public: Tag tag; int idx; // pointing to warp pool int occ; // occupancy vector int accessed; // is the entry accessed this cycle tag2warp_entry_t () { this->reset(); } ~tag2warp_entry_t () {} tag2warp_entry_t (const tag2warp_entry_t& p) { this->tag = p.tag; this->idx = p.idx; this->occ = p.occ; this->accessed = p.accessed; } tag2warp_entry_t& operator=(const tag2warp_entry_t& p) { if (this != &p) { tag = p.tag; idx = p.idx; occ = p.occ; accessed = p.accessed; } return *this; } inline bool operator==(const tag2warp_entry_t& p) const { return(tag == p.tag); } inline bool operator==(const Tag& test_tag) const { return(tag == test_tag); } inline bool operator()(const tag2warp_entry_t& p) const { return(tag == p.tag); } inline void reset() { tag.reset(); idx = 0; occ = 0; accessed = 0; } void print( FILE *fout ) { static char buffer[20]; tag.to_print(buffer,20); fprintf(fout, "\t%s->%03d (%02x)\n", buffer, idx, occ); } }; template class tag2warp_set { public: vector< tag2warp_entry_t > entry; list< tag2warp_entry_t* > lru_stack; tag2warp_set(int assoc = 0) : entry(assoc) { for (unsigned j=0; jentry.size(); j++) { this->lru_stack.push_back(&(this->entry[j])); } } tag2warp_set(const tag2warp_set& other) : entry(other.entry.size()) { for (unsigned j=0; jentry.size(); j++) { this->lru_stack.push_back(&(this->entry[j])); } } tag2warp_set& operator=(const tag2warp_set& p) { printf("tag2warp_set assignment operator called!\n"); return *this; } ~tag2warp_set() {} }; template class warp_lut { public: virtual ~warp_lut() {} virtual tag2warp_entry_t* lookup_pc2warp( const Tag& tag, bool& lut_missed ) = 0; virtual void invalidate_entry( tag2warp_entry_t* lut_entry, int warp_idx ) = 0; virtual void clear_accessed( ) = 0; virtual void print( FILE* fout) = 0; }; template class warp_lut_sa : public warp_lut { private: int lut_size; int lut_assoc; vector< tag2warp_set > tag2warp_lut; int insn_size_lgb2; queue< tag2warp_entry_t* > lut_accessed_q; // store accessed lut entry for clear struct same_tag { Tag tag; bool operator()(tag2warp_entry_t* a) { return(a->tag == tag); } }; static unsigned int lut_aliased; public: warp_lut_sa(int lut_size, int lut_assoc, int insn_size) { this->lut_size = lut_size; this->lut_assoc = lut_assoc; // optimize for LUT hash function insn_size_lgb2 = 0; while ( (1 << insn_size_lgb2) < insn_size ) insn_size_lgb2++; // initialize the pc2warp LUT // note: lut_size is the absolute size of LUT regardless of assoc. this->tag2warp_lut.assign(lut_size/lut_assoc, tag2warp_set(lut_assoc)); // assert on #set in LUT to be power of 2 int lut_nset_pow2 = 1; while ( lut_nset_pow2 < (int)tag2warp_lut.size() ) lut_nset_pow2 <<= 1; assert((int)tag2warp_lut.size() == lut_nset_pow2); } tag2warp_entry_t* lookup_pc2warp( const Tag& tag, bool& lut_missed ); void invalidate_entry( tag2warp_entry_t* lut_entry, int warp_idx ) { if (lut_entry != NULL) { // check for warp lut entry invalidation if (lut_entry->idx == warp_idx) { lut_entry->reset(); } } } void clear_accessed( ); void print( FILE* fout) { for (unsigned i=0; i< tag2warp_lut.size(); i++) { for (unsigned j=0; j< tag2warp_lut[i].entry.size(); j++) { fprintf(fout, "lut%03d-%02d:", i, j); tag2warp_lut[i].entry[j].print(fout); } } } static void print_stats ( FILE* fout ) { fprintf( fout, "lut_aliased = %d\n", lut_aliased); } }; template unsigned int warp_lut_sa::lut_aliased = 0; // lookup function in LUT // may return an entry that has different PC for replacement // or return a NULL pointer to indicate that the entry is accessed by another port template tag2warp_entry_t* warp_lut_sa::lookup_pc2warp( const Tag &tag, bool &lut_missed ) { tag2warp_entry_t* lut_entry = NULL; unsigned hashed_pc = tag.lut_hash(insn_size_lgb2, tag2warp_lut.size()); list< tag2warp_entry_t* > &hashed_lru_stack = tag2warp_lut.at(hashed_pc).lru_stack; struct same_tag same_tag_f; same_tag_f.tag = tag; typename list< tag2warp_entry_t* >::iterator lut_it; lut_it = find_if(hashed_lru_stack.begin(), hashed_lru_stack.end(), same_tag_f); if (lut_it != hashed_lru_stack.end()) { lut_entry = *lut_it; lut_entry->accessed = 1; lut_accessed_q.push(lut_entry); hashed_lru_stack.splice(hashed_lru_stack.end(), hashed_lru_stack, lut_it); assert(lut_entry == hashed_lru_stack.back()); lut_missed = false; } else { assert(!hashed_lru_stack.empty()); lut_entry = hashed_lru_stack.front(); if (lut_entry->accessed) { lut_entry = NULL; } else { lut_entry->accessed = 1; lut_accessed_q.push(lut_entry); hashed_lru_stack.splice(hashed_lru_stack.end(), hashed_lru_stack, hashed_lru_stack.begin()); assert(lut_entry == hashed_lru_stack.back()); lut_aliased++; } lut_missed = true; } assert(hashed_lru_stack.size() == tag2warp_lut[hashed_pc].entry.size()); return lut_entry; } template void warp_lut_sa::clear_accessed( ) { while ( !lut_accessed_q.empty() ) { lut_accessed_q.front()->accessed = 0; lut_accessed_q.pop(); } } // a perfect warp lut that never misses. template class warp_lut_perfect : public warp_lut { private: typedef map< Tag, tag2warp_entry_t* > warp_map_t; warp_map_t m_tag2entry_map; static unsigned int lut_max_size; public: warp_lut_perfect() {} ~warp_lut_perfect() { typename warp_map_t::iterator mit = m_tag2entry_map.begin(); for (; mit != m_tag2entry_map.end(); mit++) { delete mit->second; } } // idealistic implementation of lookup: the entry is never aliased, // and a new one is created automatically if it does not exist tag2warp_entry_t* lookup_pc2warp( const Tag& tag, bool& lut_missed ) { typename warp_map_t::iterator mit = m_tag2entry_map.find(tag); tag2warp_entry_t* lut_entry = NULL; if (mit != m_tag2entry_map.end()) { lut_entry = mit->second; assert(lut_entry->tag == tag); } else { lut_entry = new tag2warp_entry_t(); m_tag2entry_map.insert(make_pair(tag, lut_entry)); } lut_missed = false; lut_max_size = (lut_max_size < m_tag2entry_map.size())? m_tag2entry_map.size() : lut_max_size; return lut_entry; } void invalidate_entry( tag2warp_entry_t* lut_entry, int warp_idx ) { if (lut_entry == NULL) return; if (lut_entry->idx != warp_idx) return; typename warp_map_t::iterator mit = m_tag2entry_map.find(lut_entry->tag); if (mit != m_tag2entry_map.end()) { assert(mit->second == lut_entry); mit->second->reset(); delete mit->second; m_tag2entry_map.erase(mit); } } void clear_accessed( ) {} void print( FILE* fout) { typename warp_map_t::iterator mit = m_tag2entry_map.begin(); for (; mit != m_tag2entry_map.end(); mit++) { mit->second->print(fout); } } static void print_stats ( FILE* fout ) { fprintf( fout, "lut_max_size = %d\n", lut_max_size); } }; template unsigned int warp_lut_perfect::lut_max_size = 0; typedef tag2warp_entry_t warplut_entry_t; typedef pc_tag warp_tag_t; class dwf_hw_sche_class { public: int m_id; warp_lut *warp_lut_pc; vector warp_pool; deque free_warp_q; // the warp allocator int simd_width; int regf_width; int insn_size_lgb2; bool just_resume; vector m_req; // request vector from incoming warp vector m_occ_new; // occupancy vector of the new warp, double as conflict vector vector m_occ_upd; // occupancy vector of the updated existing warp vector m_occ_ext; // occupancy vector of the existing warp dwf_hw_sche_class( int lut_size, int lut_assoc, int simd_width, int regf_width, int n_threads, int insn_size, int heuristic, int id, char *policy_opt = NULL ); ~dwf_hw_sche_class(); warplut_entry_t* lookup_pc2warp( const warp_tag_t& lookup_tag ); int update_warp( int* tid, address_type pc ); // barrier handling int m_nbarriers; class dwf_barrier { public: bool m_release; // see if a barrier is to be released (ie. all warp in cta hit already) deque m_queue; // queue storing warps currently hitting a barrier, skipping warplut and scheduler dwf_barrier() : m_release(false) {} dwf_barrier(const dwf_barrier& that) : m_release(that.m_release), m_queue(that.m_queue) {} bool ready_to_issue() { return(m_release && !m_queue.empty()); } }; set< int > m_cta_released_barrier; // set of cta with released barrier map< int, dwf_barrier > m_barrier; // map int update_warp_at_barrier( int* tid, address_type pc, int cta_id, int barrier_num = 0 ); void hit_barrier( int cta_id, int barrier_num = 0 ); void release_barrier( int cta_id, int barrier_num = 0 ); int allocate_warp( address_type pc, bool update_scheduler = true ); void free_warp( int idx, bool update_warplut = true ); void issue_warp( int *tid, address_type *pc ); void clear_accessed( ) { warp_lut_pc->clear_accessed(); } void init_cta(int start_thread, int cta_size, address_type start_pc); void print_pc2warp_lut( FILE *fout ); void print_warp_pool( FILE *fout ); void print_free_warp_q( FILE *fout ); int heuristic; // FIFO warp issue logic queue issue_warp_FIFO_q; // PC warp issue logic class pc_first { public: vector &warp_pool; pc_first( vector &bp ) : warp_pool(bp) {} bool operator() (const int &idx_a, const int &idx_b) const { if (warp_pool[idx_a].pc != warp_pool[idx_b].pc) { return(warp_pool[idx_a].pc > warp_pool[idx_b].pc); } else { return(warp_pool[idx_a].occ < warp_pool[idx_b].occ); } } }; pc_first mypc_first; priority_queue, pc_first > issue_warp_PC_q; // Majority warp issue logic issue_warp_majority *issue_warp_MAJ; void clear_policy_access( ); void reset_policy_access( ); // PDOM Priority issue logic issue_warp_pdom_prio issue_warp_pdom; // statistics npc_tracker_class npc_tracker; int max_warppool_occ; int *warppool_occ_histo; // histogram of warppool occupancy static unsigned int lut_realmiss; static unsigned int uid_cnt; static unsigned int warp_fragmentation; static unsigned int warp_merge_conflict; static void print_stats ( FILE* fout ) { warp_lut_perfect::print_stats( fout ); warp_lut_sa::print_stats( fout ); fprintf( fout, "lut_realmiss = %d\n", lut_realmiss); fprintf( fout, "warp_fragmentation = %d\n", warp_fragmentation); fprintf( fout, "warp_merge_conflict = %d\n", warp_merge_conflict); } }; unsigned int dwf_hw_sche_class::lut_realmiss = 0; unsigned int dwf_hw_sche_class::uid_cnt = 0; unsigned int dwf_hw_sche_class::warp_fragmentation = 0; unsigned int dwf_hw_sche_class::warp_merge_conflict = 0; dwf_hw_sche_class::dwf_hw_sche_class( int lut_size, int lut_assoc, int simd_width, int regf_width, int n_threads, int insn_size, int heuristic, int id, char *policy_opt ) : m_id(id), // WarpLUT w/ pc tag warp_lut_pc( (lut_size == 0)? (warp_lut *) new warp_lut_perfect() : (warp_lut *) new warp_lut_sa(lut_size, lut_assoc, insn_size) ), m_nbarriers(1), // for barrier mypc_first( warp_pool ), issue_warp_PC_q( mypc_first ), // DPC issue_warp_pdom(simd_width, &warp_pool, n_threads), // DPdPri npc_tracker( NULL, simd_width ) { unsigned i; this->simd_width = simd_width; this->regf_width = regf_width; this->m_req.resize(regf_width); this->m_occ_new.resize(regf_width); this->m_occ_upd.resize(regf_width); this->m_occ_ext.resize(regf_width); // initialize the warp pool // (make sure the thread id's are init to -1) this->warp_pool.resize(n_threads); for (i=0; iheuristic = heuristic; switch (heuristic) { case MAJORITY: issue_warp_MAJ = new issue_warp_majority_queue(simd_width, &warp_pool); break; case MAJORITY_MAXHEAP: { int mh_lut_size = 32; int mh_lut_assoc = 4; int n_reads_per_cycle_lut = 4; int n_writes_per_cycle_lut = 4; int mh_size = 128; int n_reads_per_cycle_mh = 4; int n_writes_per_cycle_mh = 4; if (policy_opt != NULL) { sscanf(policy_opt, ";LUT=%d:%dr%dw%d;MH=%dr%dw%d", &mh_lut_size, &mh_lut_assoc, &n_reads_per_cycle_lut, &n_writes_per_cycle_lut, &mh_size, &n_reads_per_cycle_mh, &n_writes_per_cycle_mh); } issue_warp_MAJ = new issue_warp_majority_heap(simd_width, &warp_pool, mh_lut_size, mh_lut_assoc, mh_size, n_reads_per_cycle_lut, n_writes_per_cycle_lut, n_reads_per_cycle_mh, n_writes_per_cycle_mh); } break; } this->just_resume = false; this->max_warppool_occ = 0; this->warppool_occ_histo = new int[n_threads]; memset(this->warppool_occ_histo, 0, n_threads*sizeof(int)); } // should never be called (only at exit?) dwf_hw_sche_class::~dwf_hw_sche_class( ) { unsigned i; for (i=0; iwarppool_occ_histo; delete warp_lut_pc; } // allocate a new warp in warp pool int dwf_hw_sche_class::allocate_warp( address_type pc, bool update_scheduler ) { int idx; assert(!free_warp_q.empty()); idx = free_warp_q.front(); free_warp_q.pop_front(); warp_pool[idx].uid = uid_cnt; uid_cnt++; warp_pool[idx].pc = pc; warp_pool[idx].next_warp = -1; warp_pool[idx].lut_ptr = NULL; if (update_scheduler) { if (heuristic == FIFO) issue_warp_FIFO_q.push(idx); if (heuristic == PC) issue_warp_PC_q.push(idx); if (heuristic == MAJORITY || heuristic == MAJORITY_MAXHEAP) issue_warp_MAJ->push_warp(pc, idx); if (heuristic == PDOMPRIO) issue_warp_pdom.push_warp(pc, idx); } return idx; } // free a warp in warp pool // it will reset the content of the warp entry as well void dwf_hw_sche_class::free_warp( int idx, bool update_warplut ) { bool redundant_idx_check = false; if (redundant_idx_check) { deque::iterator dit = find(free_warp_q.begin(), free_warp_q.end(), idx); assert(dit == free_warp_q.end()); } warp_pool[idx].pc = -1; memset(warp_pool[idx].tid, -1, sizeof(int)*simd_width); warp_pool[idx].occ = 0; warp_pool[idx].next_warp = -1; if (update_warplut) { warp_lut_pc->invalidate_entry( (warplut_entry_t*)warp_pool[idx].lut_ptr, idx ); } free_warp_q.push_back(idx); assert(free_warp_q.size() <= warp_pool.size()); } warplut_entry_t* dwf_hw_sche_class::lookup_pc2warp( const warp_tag_t& lookup_tag ) { bool lut_missed = false; warplut_entry_t* lut_entry; lut_entry = warp_lut_pc->lookup_pc2warp( lookup_tag, lut_missed ); if (!lut_missed) { if (lut_entry->tag != warp_pool[lut_entry->idx].pc) lut_missed = true; } if (lut_missed) { if (npc_tracker.pc_count.find(lookup_tag.get_pc()) != npc_tracker.pc_count.end()) { lut_realmiss++; // ie. the incoming warp lost an opportunity to merge } } return lut_entry; } void fill_all (vector& container, const char& value) { fill(container.begin(), container.end(), value); } int regfile_hash(signed istream_number, unsigned simd_size, unsigned n_banks); int dwf_hw_sche_class::update_warp( int *tid, address_type pc ) { int i; bool newwarp = false; bool newwarp_alloc = false; warplut_entry_t* lut_entry; warp_tag_t warp_tag(pc); lut_entry = lookup_pc2warp(warp_tag); // no LUT entry returned, stall if (!lut_entry) { assert(0); } if (heuristic == MAJORITY || heuristic == MAJORITY_MAXHEAP) { issue_warp_MAJ->add_threads(pc, tid); } npc_tracker.add_threads( tid, pc ); // if the pc of the LUT entry does not match, // allocate a new entry if (lut_entry->tag != warp_tag) { lut_entry->idx = allocate_warp(pc); lut_entry->tag = warp_tag; lut_entry->occ = 0; assert(warp_pool[lut_entry->idx].pc == pc); newwarp = true; newwarp_alloc = true; } // create the request vector bool tid_has_valid_entry = false; fill_all(m_req, 0); for (i = 0; iidx; // create the conflict vector fill_all(m_occ_ext, 0); int regf_mask = regf_width - 1; for (i = 0; iocc & (1 << i)) == 0)? 0 : 1; } fill_all(m_occ_upd, 0); fill_all(m_occ_new, 0); int n_regf_slot = simd_width / regf_width; bool conflict = false; for (i = 0; i n_regf_slot) { m_occ_new[i] = m_occ_ext[i] + m_req[i] - n_regf_slot; m_occ_upd[i] = n_regf_slot - m_occ_ext[i]; conflict = true; } else { m_occ_upd[i] = m_req[i]; } } // if the pc of the warp mismatch with lut, // set conflict vector to all one. // that force all threads to the newly allocated warp if (warp_pool[old_idx].pc != pc) { conflict = true; for (i = 0; iidx = new_idx; lut_entry->occ = 0; //update the lut_entry assert(warp_pool[new_idx].pc == pc); int total_occ = 0; for (i = 0; i < regf_width; i++) total_occ += m_occ_ext[i] + m_req[i]; if (total_occ <= simd_width) warp_fragmentation += 1; warp_merge_conflict += 1; newwarp_alloc = true; } // update the warp as indicated by the LUT // if the lane is conflicted, or the old warp is just not // write to the new warp int new_occ = 0; fill_all(m_occ_new, 0); for (i = 0; iocc |= (1<occ = new_occ; issue_warp_pdom.add_threads(new_idx, pc); } warp_pool[lut_entry->idx].lut_ptr = lut_entry; // link up the lut entry and warp bool scheduler_consistency_check = false; if (scheduler_consistency_check && heuristic == MAJORITY) { ((issue_warp_majority_queue*)issue_warp_MAJ)->check_consistency(); } return 1; } // called AFTER threads hit a barrier to insert them into the barrier queue // ASSUME: threads from released barrier are not hitting second barrier right away int dwf_hw_sche_class::update_warp_at_barrier( int* tid, address_type pc, int cta_id, int barrier_num ) { assert(barrier_num < m_nbarriers); assert(cta_id >= 0); int i; int warp_index = 0xDEADBEEF; npc_tracker.add_threads( tid, pc ); // always allocate new warp warp_index = allocate_warp(pc, false); assert(warp_pool[warp_index].pc == pc); // no need to create the request vector // no need to create the conflict vector // assign threads into the new warp fill_all(m_occ_ext, 0); int max_nthreads_per_rfbank = simd_width / regf_width; for (i = 0; i= 0); m_barrier[cta_id].m_release = false; } // called at decode stage when all thread in cta hit the barrier // ASSUME: threads from released barrier are not hitting second barrier right away void dwf_hw_sche_class::release_barrier( int cta_id, int barrier_num ) { assert(barrier_num < m_nbarriers); assert(cta_id >= 0); map::iterator i_barrier = m_barrier.find(cta_id); assert(i_barrier != m_barrier.end()); // barrier has to exists in the first place! i_barrier->second.m_release = true; } void dwf_hw_sche_class::issue_warp( int *tid, address_type *pc ) { int i; bool warp_issued = false; // scan the released barriers for ready warp // TODO: arbitrate between different queues? set::iterator i_ctabar = m_cta_released_barrier.begin(); for (; i_ctabar != m_cta_released_barrier.end(); ++i_ctabar) { int cta_id = *i_ctabar; map::iterator i_barrier = m_barrier.find(cta_id); if ( i_barrier->second.ready_to_issue() ) { int warp_idx = i_barrier->second.m_queue.front(); for (i = 0; i < simd_width; i++) { tid[i] = warp_pool[warp_idx].tid[i]; } *pc = warp_pool[warp_idx].pc; i_barrier->second.m_queue.pop_front(); free_warp(warp_idx, false); // don't update warplut as the warp is not linked to it // remove cta from checking list if the queue is emptied // (if the last threads haven't made it back to scheduler in time, // update_warp_at_barrier will insert the cta id again) if (i_barrier->second.m_queue.empty()) { m_cta_released_barrier.erase(i_ctabar); } warp_issued = true; break; } } if (!warp_issued) { switch (heuristic) { case FIFO: // Oldest warp are issued first if (!issue_warp_FIFO_q.empty()) { int idx = issue_warp_FIFO_q.front(); for (i = 0; i < simd_width; i++) { tid[i] = warp_pool[idx].tid[i]; } *pc = warp_pool[idx].pc; issue_warp_FIFO_q.pop(); free_warp(idx); } else { memset(tid, -1, sizeof(int)*simd_width); *pc = -1; } break; case PC: // lowest PC warp are issued first if (!issue_warp_PC_q.empty()) { int idx = issue_warp_PC_q.top(); for (i = 0; i < simd_width; i++) { tid[i] = warp_pool[idx].tid[i]; } *pc = warp_pool[idx].pc; issue_warp_PC_q.pop(); free_warp(idx); } else { memset(tid, -1, sizeof(int)*simd_width); *pc = -1; } break; case MAJORITY: case MAJORITY_MAXHEAP: // issue the most common PC first { int idx = issue_warp_MAJ->pop_warp(); if (idx >= 0) { for (i = 0; i < simd_width; i++) { tid[i] = warp_pool[idx].tid[i]; } *pc = warp_pool[idx].pc; free_warp(idx); } else { memset(tid, -1, sizeof(int)*simd_width); *pc = -1; } } break; case PDOMPRIO: // issue the warp with lowest PDOM count { int idx = issue_warp_pdom.front_warp(); if (idx >= 0) { issue_warp_pdom.pop_warp(); for (i = 0; i < simd_width; i++) { tid[i] = warp_pool[idx].tid[i]; } *pc = warp_pool[idx].pc; free_warp(idx); just_resume = false; } else { memset(tid, -1, sizeof(int)*simd_width); *pc = -1; } } break; default: printf("Unsupported Heuristics!\n"); abort(); break; } } npc_tracker.sub_threads( tid, *pc ); int warppool_occ = warp_pool.size() - free_warp_q.size(); if (max_warppool_occ < warppool_occ) { max_warppool_occ = warppool_occ; } warppool_occ_histo[warppool_occ] += 1; } void dwf_hw_sche_class::init_cta(int start_thread, int cta_size, address_type start_pc) { assert((start_thread % simd_width) == 0); // thread id starting at a warp int n_warp_2assign = cta_size / simd_width; n_warp_2assign += (cta_size % simd_width)? 1 : 0; // round up static int *thd_id = NULL; if (thd_id == NULL) thd_id = new int[simd_width]; for (int w = 0; w < n_warp_2assign; w++) { // generate the warp update register for each warp fill_n(thd_id, simd_width, -1); int warp_start_tid = start_thread + w * simd_width; for (int i = 0; (i < simd_width) && (warp_start_tid + i) < (start_thread + cta_size); i++) { thd_id[i] = warp_start_tid + i; } // push these warps into DWF scheduler update_warp( thd_id, start_pc ); } } void dwf_hw_sche_class::print_free_warp_q( FILE *fout ) { fprintf(fout, "free_node_q (%zd)= ", free_warp_q.size() ); deque::iterator dit = free_warp_q.begin(); for (; dit != free_warp_q.end(); dit++) { fprintf(fout, "%03d ", *dit); } fprintf(fout, "\n"); } void print_warp( FILE *fout, warp_entry_t warp_e, int simd_width ) { fprintf(fout, "\t%02d 0x%08x: (", warp_e.pdom_prio, warp_e.pc ); for (int i=0;iclear_access( ); } } void dwf_hw_sche_class::reset_policy_access( ) { if (heuristic == MAJORITY_MAXHEAP) { ((issue_warp_majority_heap*)issue_warp_MAJ)->reset_access( ); } } /////////////////////////////////////////////////////////////////////////// // c-wrapper interface /////////////////////////////////////////////////////////////////////////// int dwf_hw_n_sche = 0; dwf_hw_sche_class **dwf_hw_sche; unsigned *acc_dyn_pcs = NULL; void create_dwf_schedulers( int n_shaders, int lut_size, int lut_assoc, int simd_width, int regf_width, int n_threads, int insn_size, int heuristic, char *policy_opt ) { dwf_hw_n_sche = n_shaders; dwf_hw_sche = new dwf_hw_sche_class*[n_shaders]; for (int i=0; inpc_tracker.acc_pc_count = &acc_dyn_pcs[i]; } } int dwf_update_warp( int shd_id, int* tid, address_type pc ) { return dwf_hw_sche[shd_id]->update_warp( tid, pc ); } int dwf_update_warp_at_barrier( int shd_id, int* tid, address_type pc, int cta_id ) { return dwf_hw_sche[shd_id]->update_warp_at_barrier( tid, pc, cta_id); } void dwf_hit_barrier( int shd_id, int cta_id ) { dwf_hw_sche[shd_id]->hit_barrier( cta_id ); } void dwf_release_barrier( int shd_id, int cta_id ) { dwf_hw_sche[shd_id]->release_barrier( cta_id ); } void dwf_issue_warp( int shd_id, int *tid, address_type *pc ) { dwf_hw_sche[shd_id]->issue_warp( tid, pc ); } void dwf_clear_accessed( int shd_id ) { dwf_hw_sche[shd_id]->clear_accessed( ); } void dwf_clear_policy_access( int shd_id ) { dwf_hw_sche[shd_id]->clear_policy_access( ); } void dwf_reset_policy_access( int shd_id ) { dwf_hw_sche[shd_id]->reset_policy_access( ); } void dwf_init_CTA(int shd_id, int start_thread, int cta_size, address_type start_pc) { dwf_hw_sche[shd_id]->init_cta(start_thread, cta_size, start_pc); dwf_hw_sche[shd_id]->clear_accessed( ); dwf_hw_sche[shd_id]->clear_policy_access( ); } void dwf_print_stat( FILE* fout ) { dwf_hw_sche_class::print_stats( fout ); npc_tracker_class::histo_print( fout ); fprintf(fout, "max_warppool_occ = "); for (int i=0; imax_warppool_occ); } fprintf(fout, "\n"); for (int i=0; iwarppool_occ_histo[j]); } fprintf(fout, "\n"); } if (dwf_hw_sche[0]->heuristic == MAJORITY_MAXHEAP) { fprintf(fout, "n_stall_on_maxheap = "); for (int i=0; iissue_warp_MAJ)->n_stall_on_maxheap); } fprintf(fout, "\n"); fprintf(fout, "maxheap_n_entries = "); for (int i=0; iissue_warp_MAJ)->maxheap.max_n_entries); } fprintf(fout, "\n"); fprintf(fout, "maxheap_lut_n_aliased = "); for (int i=0; iissue_warp_MAJ)->mh_lut.n_aliased); } fprintf(fout, "\n"); issue_warp_majority_heap::print_stat(fout); } } void dwf_reset_reconv_pt() { issue_warp_pdom_prio::reconvgence_pt.clear(); } void dwf_insert_reconv_pt(address_type pc) { issue_warp_pdom_prio::reconvgence_pt.insert(pc); } void dwf_reinit_schedulers( int n_shaders ) { for (int i=0; iissue_warp_pdom.reinit(); } } void dwf_update_statistics( int shader_id ) { dwf_hw_sche[shader_id]->npc_tracker.update_acc_count(); } void g_print_dmaj_scheduler(int sid) { dwf_hw_sche[sid]->issue_warp_MAJ->print(stdout); } void g_print_warp_lut(int sid) { dwf_hw_sche[sid]->warp_lut_pc->print(stdout); } void g_print_free_warp_q(int sid) { dwf_hw_sche[sid]->print_free_warp_q(stdout); } void g_print_warp_pool(int sid) { dwf_hw_sche[sid]->print_warp_pool(stdout); } void g_print_max_heap(int sid) { dwf_hw_sche[sid]->issue_warp_MAJ->print(stdout); } #ifdef UNIT_TEST #undef UNIT_TEST #include "stat-tool.cc" unsigned gpgpu_thread_swizzling = 0; unsigned long long gpu_sim_cycle = 0; int regfile_hash(signed istream_number, unsigned simd_size, unsigned n_banks) { if (gpgpu_thread_swizzling) { signed warp_ID = istream_number / simd_size; return((istream_number + warp_ID) % n_banks); } else { return(istream_number % n_banks); } } int log2i(int n) { int lg; lg = -1; while (n) { n>>=1;lg++; } return lg; } int test_FIFO() { dwf_hw_sche_class *dwf_sche; int i; int tid[6][4] = { { 0, 1, 2, 3}, { 4, 5, 6, 7}, { 8,-1,10,-1}, {-1, 1,-1, 3}, { 4, 9,-1,11}, {-1,13,14,-1} }; int expect_out[12][4] = { { 0, 1, 2, 3}, { 0, 1, 2, 3}, { 0, 1, 2, 3}, { 4, 5, 6, 7}, { 8, 1,10, 3}, { 4, 9,14,11}, {-1,13,-1,-1}, { 4, 9,-1,11}, {-1,13,14,-1}, { 8,-1,10,-1}, { 4, 9,14,11}, { 8,13,10,-1} }; int tid_out[4]; address_type pc_out; dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, FIFO); // same threads - different pc dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[0], 0x409a80); dwf_sche->update_warp(tid[0], 0x409a88); // different threads - different pc dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[0], 0x409a90); dwf_sche->update_warp(tid[1], 0x409a80); // different threads - same pc // expect two warp to merge into one as there is no lane conflict dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[2], 0x409a90); dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[3], 0x409a90); // same as above, but with lane conflict // expect a new warp allocated, // but only the conflicting threads goes to new warp dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[4], 0x409a80); dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[5], 0x409a80); // different threads - different pc // purposely try to alias an existing mapping dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[4], 0x410a80); dwf_sche->update_warp(tid[5], 0x411a80); // going back to that mapping // a new warp should be allocated (despite lack of conflict) dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[2], 0x409a80); // testing the occupancy vector dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[4], 0x409aa0); dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[5], 0x409aa0); dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[2], 0x409aa0); // fill the warp pool up for (i=12; i<64; ) { dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[1], 0x409a80 + 8 * i++); dwf_sche->update_warp(tid[4], 0x409a80 + 8 * i++); } // issue all the warp (do some auto checking on the way) for (i=0; i<64; i++) { dwf_sche->issue_warp(tid_out, &pc_out); printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); if (i<12) { if ( memcmp(tid_out, expect_out[i], 4*sizeof(int) ) ) { printf("%d warp mismatches\n", i); assert(0); } } } // now that all warpes are issue, no entries in the lut is valid // updating warp with an old address that remains in the lut // to see if detects the invalid lut entry dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[2], 0x409a80 + 8 * 63); dwf_sche->update_warp(tid[3], 0x409a80 + 8 * 62); dwf_sche->issue_warp(tid_out, &pc_out); assert(!memcmp(tid_out, tid[2], 4*sizeof(int) )); dwf_sche->issue_warp(tid_out, &pc_out); assert(!memcmp(tid_out, tid[3], 4*sizeof(int) )); dwf_sche->print_warp_pool(stdout); dwf_sche->warp_lut_pc->print(stdout); dwf_hw_sche_class::print_stats(stdout); delete dwf_sche; return 0; } int test_PC () { dwf_hw_sche_class *dwf_sche; int i; int tid[4][4] = { { 0, 1, 2, 3}, { 4, 5, 6, 7}, { 8,-1,10,-1}, {-1,13,14,-1} }; int tid_out[4]; address_type pc_out; dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, PC); // fill the warp pool up in reverse PC order for (i=0; i<4; i++) { for (int j=0; j<4; j++) { dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[j], 0x409a80 - 8 * i); } } // issue the warps, expect them to be in PC order, with higher occ warp issued first printf("PC Issue Logic:\n"); for (i=0; i<4; i++) { for (int j=0; j<4; j++) { dwf_sche->issue_warp(tid_out, &pc_out); printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); } } } int test_MAJ () { dwf_hw_sche_class *dwf_sche; int i; int tid[4][4] = { { 0, 1, 2, 3}, { 4, 5, 6, 7}, { 8,-1,10,-1}, {-1,13,14,-1} }; int tid_out[4]; address_type pc_out; dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, MAJORITY); // fill the warp pool up in reverse PC order for (i=0; i<4; i++) { for (int j=0; j<(4-i); j++) { dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[j], 0x409a80 - 8 * i); } } // issue the warps, expect them to be in PC order, with higher occ warp issued first printf("Majority Issue Logic:\n"); for (i=0; i<4; i++) { for (int j=0; j<4; j++) { dwf_sche->issue_warp(tid_out, &pc_out); printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); } } } int test_MAJ_HEAP () { printf("\ntest_MAJ_HEAP:\n"); dwf_hw_sche_class *dwf_sche; int i; int tid[4][4] = { { 0, 1, 2, 3}, { 4, 5, 6, 7}, { 8,-1,10,-1}, {-1,13,14,-1} }; int tid_out[4]; address_type pc_out; dwf_sche = new dwf_hw_sche_class(16, 2, 4, 4, 16, 1, MAJORITY_MAXHEAP); // fill the warp pool up in reverse PC order for (i=0; i<4; i++) { for (int j=0; j<(i+1); j++) { dwf_sche->clear_accessed(); dwf_sche->update_warp(tid[j], 0x409a80 + 8 * i); } } dwf_sche->reset_policy_access(); dwf_sche->issue_warp_MAJ->print(stdout); // issue the warps, expect them to be in PC order, with higher occ warp issued first printf("Majority (Max Heap) Issue Logic:\n"); for (i=0; i<4; i++) { for (int j=0; j<4; j++) { dwf_sche->issue_warp(tid_out, &pc_out); printf("0x%08x [%d %d %d %d]\n", pc_out, tid_out[0], tid_out[1], tid_out[2], tid_out[3]); } dwf_sche->reset_policy_access(); } } void test_warp_lut_pc () { printf("\ntest_warp_lut_pc:\n"); warp_lut_sa warp_lut_pc(16, // size 4, // assoc 1); // insn_size address_type pc_value[] = {0, 4, 0, 8, 12, 16, 20, 8, 8, 0}; int n_entry = sizeof(pc_value) / sizeof(address_type); vector pc_stream(pc_value, pc_value + n_entry); int misses = 0; for (int n = 0; n < n_entry * 100; n++) { int i = n % n_entry; tag2warp_entry_t *lut_entry = NULL; bool lut_miss = false; lut_entry = warp_lut_pc.lookup_pc2warp(pc_stream[i], lut_miss); if (lut_entry->tag != pc_stream[i]) { lut_entry->tag = pc_stream[i]; lut_entry->occ = 1; misses += 1; } warp_lut_pc.clear_accessed(); lut_entry->accessed = 0; } printf("Number of Miss = %d\n", misses); } int main () { //test_FIFO(); //test_PC(); //test_MAJ(); test_MAJ_HEAP(); test_warp_lut_pc(); return 0; } #endif