📄 mpid_rma_common.c
字号:
* * (See MPIDU_free_resource()) This does not take into account * any additional allocations done by the element type. Whether * any such buffers need to be freed depends on how the element- * type re-uses elements (when taken off the free list). * * \param[in] qhead Queue Head * \param[in] el Element object * \param[in] pe Parent element object, or NULL if 'el' * is at top of queue. * \return nothing * * \ref rsrc_design */void MPIDU_free_element(struct mpid_qhead *qhead, void *el, void *pe) { struct mpid_resource *lq = qhead->blocks; struct mpid_element *wp = el; struct mpid_element *pp = pe; MPID_assert_debug(lq != NULL && wp != NULL); /* * sanity check - 'pp' must be parent of 'wp' * or 'wp' must be at qhead->next_used. */ MPID_assert_debug(pp == NULL || pp->next == wp); MPID_assert_debug(pp != NULL || lq->next_used == wp); if (lq->last_used == wp) { lq->last_used = pp; } if (pp) { pp->next = wp->next; } else { lq->next_used = wp->next; } wp->next = lq->next_free; lq->next_free = wp;}/** * \brief Pop first element off used list (top of queue). * * Element contents is copied into 'el', if not NULL. * Popped element is placed on free list. * Returns 0 (success) if element was popped, or 1 if list empty. * * \param[in] qhead Queue Head * \param[out] el Element contents buffer * \return 1 if no elements on queue or * 0 on success with 'el' filled-in. * * \ref rsrc_design */int MPIDU_pop_element(struct mpid_qhead *qhead, void *el) { struct mpid_element *wp; struct mpid_resource *lq = qhead->blocks; if (lq == NULL || lq->next_used == NULL) { return 1; } wp = lq->next_used; if (el != NULL) { memcpy(el, wp, qhead->len); } /* we know there was no parent... */ MPIDU_free_element(qhead, wp, NULL); return 0;}/** * \brief Find specific element in queue. * * Find element in used list that "matches" according to * 'func'('el', ...). 'func' is called with arbitrary parameter 'el' * and pointer to element under test. Only one element is found, * always the first "match". 'func' returns 0 for match (success). * * Returns NULL if no match found. * If 'parent' is not NULL, returns pointer to parent element there. * Note, '*parent' == NULL means element is first in list. * * \param[in] qhead Queue Head * \param[in] func Function to use to test for desired element * \param[in] v3 void arg passed to \e func in 3rd arg * \param[in] el Static first parameter for 'func' * \param[in,out] parent Pointer to parent element to start search from; * Pointer to parent element of match found, * or NULL if 'el' is at top of queue. * \return Pointer to element found with 'parent' set, * or NULL if not found. * * \ref rsrc_design */void *MPIDU_find_element(struct mpid_qhead *qhead, int (*func)(void *, void *, void *), void *v3, void *el, struct mpid_element **parent) { struct mpid_element *wp, *pp = NULL; if (qhead->blocks == NULL) { return NULL; } wp = (parent && *parent ? (*parent)->next : qhead->blocks->next_used); if (wp) { if (!func(el, wp, v3)) { // we don't remove it here... //qhead->blocks->next_used = wp->next; } else { for (pp = wp; pp->next && func(el, pp->next, v3); pp = pp->next); wp = pp->next; } } if (parent) { *parent = pp; } return wp;}/* * * * * * Remote (origin, foreign) Datatype cache * * * * * *//** * \page dtcache_design Datatype Cache Design * * The datatype cache element stores the rank, datatype handle * and the localized datatype object (map and iovec). Builtin * datatypes are not cached (and not sent). * * This cache is used in a split fashion, where "cloned" * cache entries exist on the origin side to tell the origin * when it can skip (re-)sending the datatype. On the target * side the datatype will be fully allocated for each origin. * Because a node may be both an origin at one time and * a target at another, cache entries must be separated since * the handles in the two cases might match but do not indicate * the same datatype. Entries that are origin side dataypes have * the (target) rank with the high bit set. This prevents a * collision between local datatypes we send to that target * and foreign datatypes sent to us from that target. * * Datatype transfers are done in two sends. * * - The first send * consists of the \e MPID_Type_map structure, as generated on * the origin node. * - The second send is the datatype's \e DLOOP_VECTOR, which * defines the contiguous, type-less, regions. * * The actual (original) map and iovec are created/stored in a cache entry * under the origin node. Since the origin node never talks to itself, * this cache entry will never conflict with any remote datatype caching. * * Before any sends are done on the origin node, an attempt is made * to create a new cache entry for this datatype/target rank pair. * If this succeeds, then the datatype has not been sent to the * target before and so will be sent now. Otherwise the entire * transfer of the datatype will be skipped. * * When the target node receives the first send, the callback * attempts to create a datatype cache entry for the datatype/origin * pair. Then a handle-object is created and a receive is setup * into the handle-object map buffer. * * When the target node receives the second send, the callback * allocates a buffer for the iovec. It then sets up to * receive into the dataloop buffer. * * In order to facilitate/optimize cache flushing, a remote (target) * node always receives a datatype that is sent, even if it already * has a cache entry (i.e. it overwrites any existing cache data). * This means that the origin node must only flush its own, local, cache * when a datatype goes away, and if/when a new datatype uses the * same handle then the target side will get a new copy and replace * the old one. *//** \brief Number of Datatype Cache elements per allocation block */#define MPIDU_NUM_DTC_ENTRIES 7/** * \brief Datatype Cache Element */struct mpid_dtc_entry { struct mpid_dtc_entry *next; /**< next used or next free */ int lpid; /**< origin lpid, or target lpid | MSB */ MPI_Datatype dt; /**< datatype handle on origin */ int _pad; /**< pad to power of two size */ mpid_dt_info dti; /**< extracted info from datatype */};/** \brief Padding for Datatype Cache Element resource block header */#define MPIDU_PAD_DTC_ENTRIES 0/** \brief Queue Head for Datatype Cache */static struct mpid_qhead dtc = MPIDU_INIT_QHEAD_DECL(MPIDU_NUM_DTC_ENTRIES, sizeof(struct mpid_dtc_entry), MPIDU_PAD_DTC_ENTRIES);/* * The following are used on the ranks passed to MPIDU_locate_dt() * (et al.), specifically in the rank embedded in the element used to * create, and search for, elements in the datatype cache. *//** \brief OR'ed with rank in datatype cache in DT receives */#define MPIDU_ORIGIN_FLAG 0/** \brief OR'ed with rank in datatype cache in DT sends */#define MPIDU_TARGET_FLAG INT_MSB/** \brief test whether a datatype cache rank is target (origin-side entry) */#define MPIDU_IS_TARGET(r) (((r) & MPIDU_TARGET_FLAG) == MPIDU_TARGET_FLAG)/** \brief extract a datatype cache rank realm (TARGET or ORIGIN) */#define MPIDU_DT_REALM(r) ((r) & INT_MSB)/** \brief extract a datatype cache rank */#define MPIDU_DT_LPID(r) ((r) & ~INT_MSB)/** * \brief Callback function to match datatype cache entry * * 'v1' is a struct mpid_dtc_entry with lpid and dt filled in with * desired origin lpid and foreign datatype handle. * 'v2' is the (currrent) struct mpid_dtc_entry being examined as * a potential match. * 'v3' optional pointer to element pointer, which will be filled * with the element that contains the already-built datatype * map and iovec, if it exists. This element is the one that * has the local node's lpid. * * \param[in] v1 Desired datatype cache pseudo-element * \param[in] v2 Datatype cache element to compare with 'v1' * \param[in] v3 Pointer to Datatype cache element pointer * where same datatype but different target * will be saved, if v3 not NULL * \return boolean indicating if 'v2' does not matche 'v1'. * * \ref dtcache_design */static int mpid_match_dt(void *v1, void *v2, void *v3) { struct mpid_dtc_entry *w1 = (struct mpid_dtc_entry *)v1; struct mpid_dtc_entry *w2 = (struct mpid_dtc_entry *)v2; if (w1->dt != w2->dt) { /* couldn't possibly match */ return 1; } if (w1->lpid == w2->lpid) { /* exact match */ return 0; } if (v3 && MPIDU_DT_LPID(w2->lpid) == mpid_my_lpid) { *((struct mpid_dtc_entry **)v3) = w2; } return 1;}/** * \brief Locate a cached foreign datatype. * * Internal use only - within datatype cache routines. * Locate a foreign (remote, origin) datatype cache object in * local cache. Returns pointer to datatype cache object. * Uses origin lpid and (foreign) datatype to match. * Flag/pointer 'new' indicates whether the object must not already exist. * If 'new' is not NULL and object exists, sets *new to "0"; or if does * not exist then create new object and set *new to "1". * If 'new' is NULL and object does not exist, returns NULL. * * \param[in] lpid Rank of origin (locker) * \param[in] dt Datatype handle to search for * \param[in] new Pointer to boolean for flag indicating * new element was created. If this is not NULL, * then a new element will be created if none exists. * \param[in] src Pointer to datatype cache element pointer * used to save "closest match" element. * \return If 'new' is false, returns pointer to * datatype cache element found, or NULL if none found. * In the case of 'new' being true, returns NULL if * datatype already exists, or a pointer to a newly-created * cache element otherwise. * * \ref dtcache_design */static struct mpid_dtc_entry *MPIDU_locate_dt(int lpid, MPI_Datatype dt, int *new, struct mpid_dtc_entry **src) { struct mpid_dtc_entry el, *ep; el.lpid = lpid; el.dt = dt; ep = MPIDU_find_element(&dtc, mpid_match_dt, src, &el, NULL); if (new) { if (ep == NULL) { /* el was untouched by (failed) MPIDU_find_element() */ memset(&el.dti, 0, sizeof(el.dti)); ep = MPIDU_add_element(&dtc, &el); *new = 1; } else { *new = 0; } } return ep;}/** * \brief Callback function to match datatype cache entry for all lpids * * 'v1' is a struct mpid_dtc_entry with dt filled in with * desired origin foreign datatype handle. * 'v2' is the (currrent) struct mpid_dtc_entry being examined as * a potential match. * * \param[in] v1 Desired datatype cache pseudo-element * \param[in] v2 Datatype cache element to compare with 'v1' * \param[in] v3 Not used. * \return boolean indicating if 'v2' does not match 'v1', match * on origin-side cache entry with same handle. * * \ref dtcache_design */static int mpid_flush_dt(void *v1, void *v2, void *v3) { struct mpid_dtc_entry *w1 = (struct mpid_dtc_entry *)v1; struct mpid_dtc_entry *w2 = (struct mpid_dtc_entry *)v2; return (!MPIDU_IS_TARGET(w2->lpid) || w1->dt != w2->dt);}/** * \brief Function to remove all datatype cache entries for specific datatype * * Should be called whenever a datatype is freed/destroyed. Alternatively, * could be called whenever a datatype is detected as having changed * (i.e. handle gets re-used). * * \param[in] dtp MPID_Datatype object to be flushed * \return number of entries flushed */static int MPIDU_flush_dt(MPID_Datatype *dtp) { struct mpid_dtc_entry el, *ep; struct mpid_element *pp = NULL; int n = 0; el.dt = dtp->handle; while ((ep = MPIDU_find_element(&dtc, mpid_flush_dt, NULL, &el, &pp)) != NULL) { if (MPIDU_DT_LPID(ep->lpid) == mpid_my_lpid) { if (ep->dti.map) MPIDU_FREE(ep->dti.map, mpi_errno, "MPIDU_flush_dt"); if (ep->dti.dtp && !ep->dti.dtp->handle) MPIDU_FREE(ep->dti.dtp, mpi_errno, "MPIDU_flush_dt"); } MPIDU_free_element(&dtc, ep, pp); ++n; } return n;}void MPIDU_dtc_free(MPID_Datatype *dtp) { (void)MPIDU_flush_dt(dtp);}#ifdef NOT_USED/** * \brief Get Datatype info for a foreign datatype
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -