From 9585542fc98b2e0e20efb56ed6b6378d8304aa37 Mon Sep 17 00:00:00 2001 From: Henry Rich Date: Mon, 4 Nov 2024 18:39:12 -0500 Subject: [PATCH] Fast path for empty { array with frame; also atom { array more cases --- jsrc/j.h | 2 +- jsrc/vfrom.c | 69 +++++++++++++++++++++++++++++++++------------------ test/g631.ijs | 7 +++--- 3 files changed, 50 insertions(+), 28 deletions(-) diff --git a/jsrc/j.h b/jsrc/j.h index 0e3dd90a1..2c73e296f 100644 --- a/jsrc/j.h +++ b/jsrc/j.h @@ -723,7 +723,7 @@ struct jtimespec jmtfclk(void); //'fast clock'; maybe less inaccurate; intended #define TOOMANYATOMSX 47 // more atoms than this is considered overflow (64-bit). i.-family can't handle more than 2G cells in array. -#define MINVIRTSIZE 32 // must have this many atoms to be virtual. This is just a suggestion, and not honoroed everywhere +#define MINVIRTSIZE 32 // must have this many atoms to be virtual. This is just a suggestion, and not honored everywhere // Whether we should do so is a tricky question. Surely, if the argument is big, since we may save a large indexed copy. // If the argument is small, the virtual is still better if it doesn't have to be realized; but it might be // realized in effect if it is unavailable for inplacing. OTOH, if the argument is indirect the virtual does diff --git a/jsrc/vfrom.c b/jsrc/vfrom.c index dc2e7e7f0..60cd5d5df 100644 --- a/jsrc/vfrom.c +++ b/jsrc/vfrom.c @@ -547,43 +547,64 @@ DF2(jtfrom){A z; ARGCHK2(a,w); I at=AT(a), wt=AT(w), ar=AR(a), wr=AR(w); if(likely(!ISSPARSE(at|wt))){ - // Handle the simple case of B01|INT|FL atom { INT|FL|BOX array, and no frame: just pluck the value. If a is inplaceable, incorpable, and DIRECT, use it - // We allow FL only if it is the same size as INT + // Handle the simple case of unboxed atom { array, and no frame: single cell // We don't process NJA through here because it might create a virtual block & we don't want NJAs rendered unmodifiable by virtual blocks - if(!((at&(NOUN&~(B01|INT|(SY_64*FL))))+(wt&(NOUN&~(INT|(SY_64*FL)|BOX)))+ar+(SGNTO0((((RANKT)jt->ranks-wr)|(wr-1))))+(AFLAG(w)&AFNJA))){ +// obsolete if(!((at&(NOUN&~(B01|INT|(SY_64*FL))))+(wt&(NOUN&~(INT|(SY_64*FL)|BOX)))+ar+(SGNTO0((((RANKT)jt->ranks-wr)|(wr-1))))+(AFLAG(w)&AFNJA))){ + if(!((at&BOX)+ar+(SGNTO0((((RANKT)jt->ranks-wr)|(wr-1))))+(AFLAG(w)&AFNJA))){ // if AR is unboxed atom and w has no frame I av; // selector value - if(likely(!SY_64||at&(B01|INT))){av=BIV0(a); // INT index - }else{ // FL index - D af=DAV(a)[0], f=jround(af); av=(I)f; - ASSERT(ISFTOIOK(f,af),EVDOMAIN); // if index not integral, complain. IMAX/IMIN will fail presently. We rely on out-of-bounds conversion to peg out one side or other (standard violation) - } - I wr1=wr-1; - if(wr1<=0){ // w is atom or list, result is atom + if(likely(at&(B01|INT))){av=BIV0(a); // B01/INT index. We don't set at=INT for B01 because we aren't sure it's OK to overwrite a, which might be NJA. Questionable analysis. + }else{ + if(likely(at&FL)){ // FL index + D af=DAV(a)[0], f=jround(af); av=(I)f; if(SY_64)at=INT; // av=index; if INT atom can hold FL atom, pretend a is INT so we can + ASSERT(ISFTOIOK(f,af),EVDOMAIN); // if index not integral, complain. IMAX/IMIN will fail presently. We rely on out-of-bounds conversion to peg out one side or other (standard violation) + }else{RZ(a=cvt(INT,a)) av=IAV(a)[0]; at=INT;} // other index - must be convertible to INT, do so + } // now av is the index + I wr1=wr-1; wr1-=REPSGN(wr1); // rank of cell of w + if((SGNIF(at,INTX)&-(wt&INT+(SY_64*FL)+BOX)&(wr-2))<0){ // w is atom or list whose atomsize is SZI; a is atom of same size, result is atom + // here moving SZI-sized atoms, which means we can put the result on top of a if a is direct inplaceable abandoned + // We focus on SZI-sized atoms because we move them without a loop and can inplace into a. If we can't inplace into a we could revert to general 1-cell code below, but we skip quite a bit here + I j; SETNDX(j,av,AN(w)); // fetch and audit index into j // Get the area to use for the result: the a input if possible (inplaceable, incorpable, DIRECT), else an INT atom. a=w OK! // We can't get away with changing the type for an INT atom a to BOX. It would work if the a is not contents, but if it is pristine contents it may have // been made to appear inplaceable in jtevery. In that case, when we change the AT we have the usecount wrong, because the block is implicitly recursive by virtue // of being contents. It's not a good trade to check for recursiveness of contents in tpop (currently implied). - if((SGNIF(jtinplace,JTINPLACEAX)&AC(a)&(((AFLAG(a)|wt)&AFUNINCORPABLE+BOX)-1))<0)z=a; else{GAT0(z,INT,1,0)} + if((SGNIF(jtinplace,JTINPLACEAX)&AC(a)&(((AFLAG(a)|wt)&AFUNINCORPABLE+AFNJA+BOX)-1))<0){z=a; AT(z)=wt;} else{GA00(z,wt,1,0)} // NJA=LIT, ok // Move the value and transfer the block-type - I j; SETNDX(j,av,AN(w)); IAV(z)[0]=IAV(w)[j]; AT(z)=wt; // change type only if the transfer succeeds, to avoid creating an invalid a block that eformat will look at - // Here we transferred one I/A out of w. We must mark w non-pristine. If it was inplaceable, we can transfer the pristine status. We overwrite w because it is no longer in use + IAV(z)[0]=IAV(w)[j]; // change type only if the transfer succeeds, to avoid creating an invalid a block that eformat will look at + // We transferred one I/A out of w. We must mark w non-pristine. If it was inplaceable, we can transfer the pristine status. We overwrite w because it is no longer in use PRISTXFERF(z,w) // this destroys w }else{ - // rank of w > 1, return virtual cell - I *ws=AS(w); // shape of w + // Not SZI-sized items. w is not INT/FL/BOX or has rank >1, return single cell, possibly virtual + I *ws=AS(w); I wi; SETIC(w,wi); // shape of w, number of items in w I m; PROD(m,wr1,ws+1); // number of atoms in a cell - I j; SETNDX(j,av,ws[0]); // j=positive index - RZ(z=virtualip(w,j*m,wr1)); // if w is rank 2, could reuse inplaceable a for this virtual block - // fill in shape and number of atoms. ar can be anything. - AN(z)=m; MCISH(AS(z),ws+1,wr1) +// obsolete I j; SETNDX(j,av,ws[0]); // j=positive index + I j; SETNDX(j,av,wi); // j=positive index, audited + if(mranks-((ar<ranks>>RANKTX); af=af<0?0:af; I wf=wr-(RANKT)jt->ranks; wf=wf<0?0:wf; I lf=aflonger frame + I cf=af+wf-lf; ASSERTAGREE(AS(a)+af-cf,AS(w)+wf-cf,cf) // cf=common frame; verify common frames agree + zr-=wf; zr=zr<0?0:zr; // remove the w frame from w rank to get the cell-rank + GA00(z,wt,0,lf+ar-af+zr); MCISH(AS(z),AS(la),lf) MCISH(AS(z)+lf,AS(a)+af,ar-af) MCISH(AS(z)+lf+ar-af,AS(w)+wr-zr,zr) // allocate the empty array & move in shape } - }else if(unlikely(AN(a)==0&&!((jt->ranks-((ar<ranks; // remember original ranks in case of error if(!(at&BOX))z=jtifrom(jtinplace,a,w);else z=jtafrom(jtinplace,a,w); // If there was an error, call eformat while we still have the ranks. convert default rank back to R2MAX to avoid "0 0 in msg diff --git a/test/g631.ijs b/test/g631.ijs index c8d775f02..4408848f7 100644 --- a/test/g631.ijs +++ b/test/g631.ijs @@ -323,10 +323,11 @@ NB. dyad doesn't support prist yet '2' +&.> ckprist 0 1 1 ] 5 NB. scaf '<"0 i. 5' ]"0 ckprist 0 1 1 1 ] 5 NB. passes y through unmodified '<"0 i. 5' ["0 ckprist 0 1 1 1 ] 5 NB. passes x through unmodified '<"0 i. 5' ["0 ckprist 0 0 2 ] 5 2 NB. y arg ignored - x is marked as repeated -'2' { ckprist 1 1 ] 4 5 NB. virtual+pristine because this goes through virtualip -'1' { ckprist 1 1 ] 4 5 +'2' { ckprist 0 1 ] 4 5 NB. pristine because too short for virtual +'2' { ckprist 1 1 ] 4 64 NB. virtual+pristine because this goes through virtualip +'1' { ckprist 1 1 ] 4 64 '1 3' { ckprist 0 0 ] 4 5 NB. not pristine, because indexes could be repeated -'2 3' { ckprist 0 0 ] 4 5 NB. virtual+pristine because this goes through virtualip +'2 3' { ckprist 0 0 ] 4 5 NB. pristine because too short for virtual '2 3' { ckprist 1 1 ] 4 50 NB. virtual+pristine because this goes through virtualip '<1' { ckprist 1 1 ] 4 50 NB. virtual block, does not clear w prist '<1' { ckprist 0 0 ] 4 1 NB. nonvirtual block, clears w prist