Project

General

Profile

Feature #34

Reduce Charm Message Send Overhead for Marshalled Messages

Added by Eric Bohm over 6 years ago. Updated over 1 year ago.

Status:
New
Priority:
High
Assignee:
Category:
-
Target version:
Start date:
02/07/2013
Due date:
02/28/2013
% Done:

0%


Description

How many lines of code does it take to send a Chare Array message down into the Converse layer?

LOC Experiment
--- -------------------------------------------------------------
388 Original (-O0 -g)
330 --with-production -O0 -g
273 as above, but message instead of marshal
193 as above, but reusing message so message construct not traced

It makes decent a difference to drop the assertions and trace instrumentation, but even after that the result is still alarmingly high.

A certain amount of overhead originates from the separation of concerns in our layered implementation. We do not know a message's destination at the time we create it, so allocating a message and populating its header fields is a multi phase operation, with distinct actions taken at allocation time and another set at send time, with one or more LOCs per envelope field of concern.

However, that statement is not true for a marshalled message. From the application perspective, the destination is determined before the message is constructed. AFAICT, we do exactly nothing to exploit that fact. This makes marshalled message sends far more expensive in implementation than they need to be conceptually. The implementation walks down the layers of abstraction in much the same way as we do for the manual message case, except we pay marshalling implementation costs on top of the usual sending approach. This suggests that an implementation of marshalling could exist which builds a completely initialized message at allocation time by using fully populated initializer constructors. This would reduce the LOCs, and increase efficiency by avoiding the overhead of setting default values and overriding them with actual values in subsequent assignment phases. I suspect it would also be a lot easier to comprehend than the current scheme.

Another sizable chunk of our LOC bill, which looks more complex than necessary for the standard use case, is the path through the CkLocMgr. Thats 73 lines of code to figure out what our destination PE is. I'm not even counting the prior code to get our array id, indices, EP, CK envelope settings for same, and the (irrelevant in this undelegated case) march through the delegation scheme to get to the CkLocMgr::deliver call (60 LOC).

Consider a neighbour communication pattern code. It will go through all that, every iteration, once for each neighbour. 99% of the time (except for the rare case in which the neighbour migrated) the CK envelope contents of the message for any particular neighbour will be the same as in the prior iteration. The persistent API would be one way to reduce this, but an API transparent change which cached the results could significantly cut the costs of sending short messages to array elements.

pingpong-multicore.png View (13.2 KB) Eric Bohm, 12/16/2014 02:59 PM

History

#1 Updated by Phil Miller over 6 years ago

  • Target version set to Unscheduled

#2 Updated by Phil Miller over 6 years ago

  • Target version changed from Unscheduled to 6.6.0

#3 Updated by Phil Miller about 6 years ago

  • Priority changed from Normal to High

#4 Updated by Eric Bohm almost 6 years ago

  • Target version changed from 6.6.0 to 6.7.0

#5 Updated by Eric Bohm over 5 years ago

justcode.txt


59          thisProxy[1].OurMsg(indata-1);
392            {return CProxyElement_StackView(ckGetArrayID(), CkArrayIndex1D(idx), CK_DELCTOR_CALL);}
352        { return CProxy_ArrayElement::ckDelegatedPtr(); }
896        { return CProxy_ArrayBase::ckDelegatedPtr(); }
645        inline CkDelegateData *ckDelegatedPtr(void) const {return delegatedPtr;}
350        { return CProxy_ArrayElement::ckDelegatedTo(); }
894        { return CProxy_ArrayBase::ckDelegatedTo(); }
630          if (delegatedMgr == NULL && !delegatedGroupId.isZero()) {
196      inline int isZero(void) const { return (idx==0); }
639          return delegatedMgr; 
90        CkArrayIndex1D(int i0) { init(1, 1, i0); }
69            CkArrayIndex() { nInts=0; dimension=0; for (int i=0; i<CK_ARRAYINDEX_MAXLEN; i++) index[i] = 0; }
151                nInts = num;
152                dimension = dims;
153                index[0] = x;
154                index[1] = y;
155                index[2] = z;
156                for (int i=3; i < CK_ARRAYINDEX_MAXLEN; i++)
361        { return CProxy_ArrayElement::ckGetArrayID(); }
905        { return CProxy_ArrayBase::ckGetArrayID(); }
346        CkArrayID ckGetArrayID(void) const { return _aid; }
297            :CProxyElement_ArrayElement(aid,idx,CK_DELCTOR_ARGS)
838            :CProxyElement_ArrayBase(aid,idx,CK_DELCTOR_ARGS)
368            :CProxy_ArrayBase(aid,CK_DELCTOR_ARGS), _idx(idx) { }
327            :CProxy(CK_DELCTOR_ARGS), _aid(aid) { }
586        :delegatedMgr(dTo)
588                delegatedPtr = NULL;
589                if(delegatedMgr != NULL && dPtr != NULL) {
839        {}
298        {}
139      ckCheck();
256        { CProxyElement_ArrayElement::ckCheck(); }
797        { CProxyElement_ArrayBase::ckCheck(); }
334          if (_aid.isZero())
215        inline int isZero(void) const {return _gid.isZero();}
196      inline int isZero(void) const { return (idx==0); }
141      int impl_off=0;
143        PUP::sizer implP;
370      sizer(void):er(IS_SIZING),nBytes(0) {}
160               er(unsigned int inType): PUP_er_state(inType) {} //You don't want to create raw PUP::er's.
144        implP|impl_noname_0;
958    PUP_BUILTIN_SUPPORT(int)
191      void operator()(int &v)             {(*this)(&v,1);}
224        {bytes((void *)a,nItems,sizeof(int),Tint);}
110        if (n<0) CmiAbort("PUP::sizer> Tried to pup a negative number of items!");
111        const unsigned int maxPupBytes=1024*1024*1024; //Pup 1 GB at a time
112        if (((unsigned int)(n*itemSize))>maxPupBytes) 
115        nBytes+=n*itemSize;
145        impl_off+=implP.size();
373      int size(void) const {return nBytes;}
363    class sizer : public er {
25    PUP::er::~er() {}
147      CkMarshallMsg *impl_msg=CkAllocateMarshallMsg(impl_off,impl_e_opts);
1044        if (opts==NULL) {
1045          CkMarshallMsg *newMemory = new (size,0)CkMarshallMsg;
23      sizes[0] = sz0;
24      return CkMarshallMsg::alloc(__idx, s, sizes, p);
27      CkpvAccess(_offsets)[0] = ALIGN8(sz);
28      if(sizes==0)
31        CkpvAccess(_offsets)[1] = CkpvAccess(_offsets)[0] + ALIGN8(sizeof(char)*sizes[0]);
32      return CkAllocMsg(msgnum, CkpvAccess(_offsets)[1], pb);
22      env = _allocEnv(ForChareMsg, msgBytes, prioBits);
389      return envelope::alloc(msgtype,size,prio);
243          CkAssert(type>=NewChareMsg && type<=ForArrayEltMsg);
246            sizeof(int)*CkPriobitsToInts(prio);
247          register envelope *env = (envelope *)CmiAlloc(tsize);
2864      res =(char *) malloc_nomigrate(size+sizeof(CmiChunkHeader));
703    void *malloc_nomigrate(size_t size) { return malloc(size); }
608      MEM_LOCK_AROUND( result = meta_malloc(size); )
905    void CmiMemLock() { memflag++; }
3312        __malloc_hook;
3313      if (hook != NULL)
3316      arena_get(ar_ptr, bytes);
3317      if(!ar_ptr)
3319      victim = _int_malloc(ar_ptr, bytes);
3824      checked_request2size(bytes, nb);
3832      if ((unsigned long)(nb) <= (unsigned long)(av->max_fast)) {
3833        fb = &(av->fastbins[(fastbin_index(nb))]);
3834        if ( (victim = *fb) != 0) {
3835          *fb = victim->fd;
3837          return chunk2mem(victim);
3320      if(!victim) {
3339        (void)mutex_unlock(&ar_ptr->mutex);
3343      if (victim!=NULL) {
3344      _memory_allocated += chunksize(mem2chunk(victim));
3346      UPDATE_MEMUSAGE
3349      return victim;
906    void CmiMemUnlock() { memflag--; }
609      if (result==NULL) CmiOutOfMemory(size);
610      return result;
2867      _MEMCHECK(res);
2889      res+=sizeof(CmiChunkHeader);
2890      SIZEFIELD(res)=size;
2891      REFFIELD(res)=1;
2892      return (void *)res;
250          memset(env, 0, sizeof(envelope));
44    ../sysdeps/x86_64/memset.S: No such file or directory.
45    in ../sysdeps/x86_64/memset.S
46    in ../sysdeps/x86_64/memset.S
50    in ../sysdeps/x86_64/memset.S
51    in ../sysdeps/x86_64/memset.S
52    in ../sysdeps/x86_64/memset.S
53    in ../sysdeps/x86_64/memset.S
55    in ../sysdeps/x86_64/memset.S
56    in ../sysdeps/x86_64/memset.S
58    in ../sysdeps/x86_64/memset.S
63    in ../sysdeps/x86_64/memset.S
64    in ../sysdeps/x86_64/memset.S
65    in ../sysdeps/x86_64/memset.S
66    in ../sysdeps/x86_64/memset.S
67    in ../sysdeps/x86_64/memset.S
329    in ../sysdeps/x86_64/memset.S
330    in ../sysdeps/x86_64/memset.S
331    in ../sysdeps/x86_64/memset.S
332    in ../sysdeps/x86_64/memset.S
333    in ../sysdeps/x86_64/memset.S
334    in ../sysdeps/x86_64/memset.S
335    in ../sysdeps/x86_64/memset.S
336    in ../sysdeps/x86_64/memset.S
337    in ../sysdeps/x86_64/memset.S
338    in ../sysdeps/x86_64/memset.S
339    in ../sysdeps/x86_64/memset.S
340    in ../sysdeps/x86_64/memset.S
251          env->setEvent(++CkpvAccess(envelopeEventID));
214        void   setEvent(const UInt e) { event = e; }
253          env->setMsgtype(type);
220        void   setMsgtype(const UChar m) { attribs.mtype = m; }
254          env->totalsize = tsize;
255          env->priobits = prio;
256          env->setPacked(0);
233        void   setPacked(const UChar p) { attribs.isPacked = p; }
257          env->type.group.dep.setZero();
197      inline void setZero(void) { idx=0; }
258          _SET_USED(env, 0);
223        void   setUsed(const UChar u) { attribs.isUsed=u; }
259          env->setRef(0);
216        void   setRef(const CMK_REFNUM_TYPE r) { ref = r; }
260          env->setEpIdx(0);
285        void   setEpIdx(const UShort idx) { epIdx = idx; }
276          return env;
23      setMemoryTypeMessage(env);
780    void setMemoryTypeMessage(void *ptr) { }
25      env->setQueueing(_defaultQueueing);
218        void   setQueueing(const UChar q) { attribs.queueing=q; }
26      env->setMsgIdx(msgIdx);
228        void   setMsgIdx(const UChar idx) { attribs.msgIdx = idx; }
28      return EnvToUsr(env);
385      return ((void *)(env+1));
176    class CkMarshallMsg : public CMessage_CkMarshallMsg {
34    CMessage_CkMarshallMsg::CMessage_CkMarshallMsg() {
52        CkMessage() {}
35    CkMarshallMsg *newmsg = (CkMarshallMsg *)this;
36      newmsg->msgBuf = (char *) ((char *)newmsg + CkpvAccess(_offsets)[0]);
1046          setMemoryTypeMessage(UsrToEnv(newMemory));
381      return (((envelope *) msg)-1);
780    void setMemoryTypeMessage(void *ptr) { }
1047          return newMemory;
149        PUP::toMem implP((void *)impl_msg->msgBuf);
406      toMem(void *Nbuf):mem(IS_PACKING,(myByte *)Nbuf) {}
386      mem(unsigned int type,myByte *Nbuf):er(type),origBuf(Nbuf),buf(Nbuf) {}
160               er(unsigned int inType): PUP_er_state(inType) {} //You don't want to create raw PUP::er's.
150        implP|impl_noname_0;
958    PUP_BUILTIN_SUPPORT(int)
191      void operator()(int &v)             {(*this)(&v,1);}
224        {bytes((void *)a,nItems,sizeof(int),Tint);}
125        n*=itemSize;
126        memcpy((void *)buf,p,n); 
56    ../sysdeps/x86_64/memcpy.S: No such file or directory.
58    in ../sysdeps/x86_64/memcpy.S
60    in ../sysdeps/x86_64/memcpy.S
63    in ../sysdeps/x86_64/memcpy.S
64    in ../sysdeps/x86_64/memcpy.S
75    in ../sysdeps/x86_64/memcpy.S
76    in ../sysdeps/x86_64/memcpy.S
87    in ../sysdeps/x86_64/memcpy.S
88    in ../sysdeps/x86_64/memcpy.S
90    in ../sysdeps/x86_64/memcpy.S
91    in ../sysdeps/x86_64/memcpy.S
93    in ../sysdeps/x86_64/memcpy.S
94    in ../sysdeps/x86_64/memcpy.S
99    in ../sysdeps/x86_64/memcpy.S
100    in ../sysdeps/x86_64/memcpy.S
111    in ../sysdeps/x86_64/memcpy.S
112    in ../sysdeps/x86_64/memcpy.S
135    in ../sysdeps/x86_64/memcpy.S
127        buf+=n;
400    class toMem : public mem {
382    class mem : public er { //Memory-buffer packers and unpackers
25    PUP::er::~er() {}
152      CkArrayMessage *impl_amsg=(CkArrayMessage *)impl_msg;
153      impl_amsg->array_setIfNotThere(CkArray_IfNotThere_buffer);
153        UsrToEnv((void *)this)->setArrayIfNotThere(i);
381      return (((envelope *) msg)-1);
364        void setArrayIfNotThere(int nt) {type.array.ifNotThere=nt;}
154      ckSend(impl_amsg, CkIndex_StackView::idx_OurMsg_marshall2(),0);
182          static int epidx = reg_OurMsg_marshall2();
183          return epidx;
287        { CProxyElement_ArrayElement::ckSend(m,ep,opts); }
828        { CProxyElement_ArrayBase::ckSend(m,ep,opts); }
1068        if (_idx.nInts<0) CkAbort("Array index length is negative!\n");
1069        if (_idx.nInts>CK_ARRAYINDEX_MAXLEN)
1073        msg_prepareSend(msg,ep,ckGetArrayID());
346        CkArrayID ckGetArrayID(void) const { return _aid; }
1038        envelope *env=UsrToEnv((void *)msg);
381      return (((envelope *) msg)-1);
1039        env->getsetArrayMgr()=aid;
357        CkGroupID &getsetArrayMgr(void) {return type.array.arr;}
216        operator CkGroupID() const {return _gid;}
1040        env->getsetArraySrcPe()=CkMyPe();
361        UInt &getsetArraySrcPe(void) {return pe;}
57    static inline int CkMyPe() { return CmiMyPe(); }
1041        env->setEpIdx(ep);
285        void   setEpIdx(const UShort idx) { epIdx = idx; }
1042        env->getsetArrayHops()=0;
362        UChar &getsetArrayHops(void) {return type.array.hopCount;}
1074        msg->array_index()=_idx;//Insert array index
129        return UsrToEnv((void *)this)->getsetArrayIndex();
381      return (((envelope *) msg)-1);
367            {return *(CkArrayIndex *)&type.array.index;}
1075        if (ckIsDelegated()) //Just call our delegateMgr
622        int ckIsDelegated(void) const { return(delegatedMgr!=NULL);}
1079          CkArray *localbranch = ckLocalBranch();
347        CkArray *ckLocalBranch(void) const { return _aid.ckLocalBranch(); }
218            { return (CkArray *)CkLocalBranch(_gid); }
213        CkArrayID(CkGroupID g) :_gid(g) {}
220            { return (CkArray *)::CkLocalBranch(id); }
216        operator CkGroupID() const {return _gid;}
505      return _localBranch(gID);
151      return CkpvAccess(_groupTable)->find(gID).getObj();
92          if(n.idx>0 && n.idx<max)
93            return tab[n.idx];
22        inline IrrGroup* getObj(void) { return obj; }
1080          if (localbranch == NULL) {             // array not created yet
1084            if (opts & CK_MSG_INLINE)
1087              localbranch->deliver(msg, CkDeliver_queue, opts);
676          {return locMgr->deliver(m,type,opts);}
2434        CK_MAGICNUMBER_CHECK
457            if (magic!=good) badMagicNumber(good,file,line,obj);
2435        CkArrayMessage *msg=(CkArrayMessage *)m;
2438        const CkArrayIndex &idx=msg->array_index();
129        return UsrToEnv((void *)this)->getsetArrayIndex();
381      return (((envelope *) msg)-1);
367            {return *(CkArrayIndex *)&type.array.index;}
2440        if (type==CkDeliver_queue)
2441            _TRACE_CREATION_DETAILED(UsrToEnv(m),msg->array_ep());
2442        CkLocRec *rec=elementNrec(idx);
3166        return hash.get(*(CkArrayIndex *)&idx);
367            int i=key.hash()%this->len;
103                register const int *d=data();
76            const int *data(void) const {return index; }
104                register CkHashCode ret=d[0];
105                for (i=1;i<nInts;i++)
107                return ret;
369                char *cur=this->entry(i);
214        char *entry(int i) const {return (char *)(table+i*layout.entrySize());}
161      inline int entrySize(void) const {return size;}
371                if (this->layout.isEmpty(cur)){ 
172      inline char isEmpty(char *entry) const {return *(entry+po);}
372                    return OBJ(0);
2452            LDObjid ldid = idx2LDObjid(idx);
76      const int *data=idx.data();
76            const int *data(void) const {return index; }
77      if (OBJ_ID_SZ>=idx.nInts) {
78        for (i=0;i<idx.nInts;i++)
79          r.id[i]=data[i];
78        for (i=0;i<idx.nInts;i++)
80        for (i=idx.nInts;i<OBJ_ID_SZ;i++)
81          r.id[i]=0;
80        for (i=idx.nInts;i<OBJ_ID_SZ;i++)
81          r.id[i]=0;
80        for (i=idx.nInts;i<OBJ_ID_SZ;i++)
81          r.id[i]=0;
80        for (i=idx.nInts;i<OBJ_ID_SZ;i++)
99      return r;
2456        if (type==CkDeliver_queue) {
2457            if (!(opts & CK_MSG_LB_NOTRACE) && the_lbdb->CollectingCommStats()) {
302      inline int  CollectingCommStats(void) { return LDCollectingStats(myLDHandle) && _lb_args.traceComm(); };
7      LBDB *const db = (LBDB*)(_db.handle);
8      return db->StatsOn();
106           { return statsAreOn; };
2543        if (rec!=NULL){
2560            if (opts & CK_MSG_KEEP)
2562            deliverUnknown(msg,type,opts);
2571        CK_MAGICNUMBER_CHECK
457            if (magic!=good) badMagicNumber(good,file,line,obj);
2572        const CkArrayIndex &idx=msg->array_index();
129        return UsrToEnv((void *)this)->getsetArrayIndex();
381      return (((envelope *) msg)-1);
367            {return *(CkArrayIndex *)&type.array.index;}
2573        int onPe=homePe(idx);
629            {return map->homePe(mapHandle,idx);}
96                 { return procNum(arrayHdl, element); }
356        if (amaps[arrayHdl]->_nelems.nInts == 0) {
221          CmiAssert(n<len);
222          return block[n]; 
455            { return storage; }
360        if (i.nInts == 1) {
361          flati = i.data()[0];
76            const int *data(void) const {return index; }
373        if(useNodeBlkMapping){
398        if(flati < amaps[arrayHdl]->_numFirstSet)
221          CmiAssert(n<len);
222          return block[n]; 
455            { return storage; }
400        else if (flati < amaps[arrayHdl]->_numChares)
221          CmiAssert(n<len);
222          return block[n]; 
455            { return storage; }
401          return (amaps[arrayHdl]->_remChares + (flati - amaps[arrayHdl]->_numFirstSet) / (amaps[arrayHdl]->_binSizeFloor));
221          CmiAssert(n<len);
222          return block[n]; 
455            { return storage; }
221          CmiAssert(n<len);
222          return block[n]; 
455            { return storage; }
221          CmiAssert(n<len);
222          return block[n]; 
455            { return storage; }
2574        if (onPe!=CkMyPe()) 
57    static inline int CkMyPe() { return CmiMyPe(); }
2577            msg->array_hops()++;
141        return UsrToEnv((void *)this)->getsetArrayHops();
381      return (((envelope *) msg)-1);
362        UChar &getsetArrayHops(void) {return type.array.hopCount;}
2578            CkArrayManagerDeliver(onPe,msg,opts);
2019      register envelope *env = UsrToEnv(msg);
381      return (((envelope *) msg)-1);
2020      _prepareOutgoingArrayMsg(env,ForArrayEltMsg);
1999      _CHECK_USED(env);
222        UChar  isUsed(void) { return attribs.isUsed; }
2000      _SET_USED(env, 1);
223        void   setUsed(const UChar u) { attribs.isUsed=u; }
2001      env->setMsgtype(type);
220        void   setMsgtype(const UChar m) { attribs.mtype = m; }
2003      setMemoryOwnedBy(((char*)env)-sizeof(CmiChunkHeader), 0);
803    void setMemoryOwnedBy(void *ptr, int id) { }
2005      CmiSetHandler(env, _charmHandlerIdx);
2006      CpvAccess(_qd)->create();
94            mCreated += n; 
96            sendCount(0, n);
209      if (_dummy_dq == 0) {
211            if (CmiImmIsRunning())
2024      if (opts & CK_MSG_IMMEDIATE)
2026      if (opts & CK_MSG_SKIP_OR_IMM)
2029        _skipCldEnqueue(pe, env, _infoIdx);
1364      if (!ConverseDeliver(pe)) {
31      return !_replaySystem && (!_conditionalDelivery || pe==CmiMyPe());
1369      if(pe == CkMyPe() ){
57    static inline int CkMyPe() { return CmiMyPe(); }
1375      if (pe == CkMyPe() && !CmiImmIsRunning()) {
57    static inline int CkMyPe() { return CmiMyPe(); }
1390        if (pe < 0 || CmiNodeOf(pe) != CmiMyNode())
1391          CkPackMessage(&env);
1301      register envelope *env = *pEnv;
1302      if(!env->isPacked() && _msgTable[env->getMsgIdx()]->pack) {
232        UChar  isPacked(void) const { return attribs.isPacked; }
227        UChar  getMsgIdx(void) const { return attribs.msgIdx; }
284            if (idx>=vec.size()) outOfBounds(idx);
288        size_t size(void) const {return len;}
286            return vec[idx];
221          CmiAssert(n<len);
222          return block[n]; 
1303        register void *msg = EnvToUsr(env);
385      return ((void *)(env+1));
1304        _TRACE_BEGIN_PACK();
1305        msg = _msgTable[env->getMsgIdx()]->pack(msg);
227        UChar  getMsgIdx(void) const { return attribs.msgIdx; }
284            if (idx>=vec.size()) outOfBounds(idx);
288        size_t size(void) const {return len;}
286            return vec[idx];
221          CmiAssert(n<len);
222          return block[n]; 
42      msg->msgBuf = (char *) ((char *)msg->msgBuf - (char *)msg);
43      return (void *) msg;
1306        _TRACE_END_PACK();
1307        env=UsrToEnv(msg);
381      return (((envelope *) msg)-1);
1308        env->setPacked(1);
233        void   setPacked(const UChar p) { attribs.isPacked = p; }
1309        *pEnv = env;
1392        int len=env->getTotalsize();
229        UInt   getTotalsize(void) const { return totalsize; }
1393        CmiSetXHandler(env,CmiGetHandler(env));
1397        CmiSetHandler(env,index_skipCldHandler);
1399        CmiSetInfo(env,infoFn);
1400        if (pe==CLD_BROADCAST) {
1411        else if (pe==CLD_BROADCAST_ALL) { 
1429                            CmiSyncSendAndFree(pe, len, (char *)env);

#6 Updated by Eric Bohm over 4 years ago

The bulk of the performance overhead here is in the copy. The extra lines of code and other inefficiencies in header population are worth considering only after we design a way to avoid the copying overhead. They will probably become irrelevant as the revised semantic to avoid copying will require its own code path, which will require its own profiling exploration, regardless.

#7 Updated by Eric Bohm over 4 years ago

To quantify matters a bit, see the figure which indicates a poor performance for marshalling in the single PE case that grows steadily worse as the message size grows (expecially over 1k). The 2 PE case is basically the same story with a little more latency.

#8 Updated by Eric Bohm over 4 years ago

Eric Bohm wrote:

To quantify matters a bit, see the figure which indicates a poor performance for marshalling in the single PE case that grows steadily worse as the message size grows (expecially over 1k). The 2 PE case is basically the same story with a little more latency.

Attached figure

#9 Updated by Eric Bohm over 3 years ago

  • Target version changed from 6.7.0 to 6.8.0

#10 Updated by Sam White over 2 years ago

  • Target version changed from 6.8.0 to 6.9.0

Does the RDMA send API address this adequately?

#11 Updated by Eric Bohm over 1 year ago

The zero copy schemes should address this issue, but we'll need to retest.

#12 Updated by Eric Bohm over 1 year ago

  • Target version changed from 6.9.0 to Unscheduled

Also available in: Atom PDF