57void SpImpl<SR,IT,NUM,IVT,OVT>::SpMXSpV(
const Dcsc<IT,NUM> & Adcsc,
int32_t mA,
const int32_t * indx,
const IVT * numx,
int32_t veclen,
58 std::vector<int32_t> & indy, std::vector< OVT > & numy)
62 std::vector< std::pair<IT,IT> > colinds( (
IT) veclen);
65 if(
sizeof(NUM) >
sizeof(OVT))
68 for(
IT j =0; j< veclen; ++j)
70 while(colinds[j].first != colinds[j].second )
72 OVT mrhs = SR::multiply(Adcsc.
numx[colinds[j].first], numx[j]);
73 if(SR::returnedSAID())
84 std::make_heap(wset, wset+hsize);
87 std::pop_heap(wset, wset + hsize);
88 IT locv = wset[hsize-1].
runr;
89 if((!indy.empty()) && indy.back() == wset[hsize-1].
key)
91 numy.back() = SR::add(numy.back(), wset[hsize-1].
num);
95 indy.push_back( (
int32_t) wset[hsize-1].key);
96 numy.push_back(wset[hsize-1].num);
100 while ( (++(colinds[locv].first)) != colinds[locv].second )
102 OVT mrhs = SR::multiply(Adcsc.
numx[colinds[locv].first], numx[locv]);
103 if(!SR::returnedSAID())
105 wset[hsize-1].
key = Adcsc.
ir[colinds[locv].first];
106 wset[hsize-1].
num = mrhs;
107 std::push_heap(wset, wset+hsize);
120 for(
IT j =0; j< veclen; ++j)
122 if(colinds[j].first != colinds[j].second)
127 std::make_heap(wset, wset+hsize);
130 std::pop_heap(wset, wset + hsize);
131 IT locv = wset[hsize-1].
runr;
132 OVT mrhs = SR::multiply(wset[hsize-1].num, numx[locv]);
134 if (!SR::returnedSAID())
136 if((!indy.empty()) && indy.back() == wset[hsize-1].
key)
138 numy.back() = SR::add(numy.back(), mrhs);
142 indy.push_back( (
int32_t) wset[hsize-1].key);
143 numy.push_back(mrhs);
147 if( (++(colinds[locv].first)) != colinds[locv].second)
150 wset[hsize-1].
key = Adcsc.
ir[colinds[locv].first];
151 wset[hsize-1].
num = Adcsc.
numx[colinds[locv].first];
152 std::push_heap(wset, wset+hsize);
396 double tstart = MPI_Wtime();
402 nthreads = omp_get_num_threads();
405 if(rowSplits < nthreads)
407 std::ostringstream outs;
408 outs <<
"Warning in SpMXSpV_Bucket: " << rowSplits <<
" buckets are supplied for " << nthreads <<
" threads\n";
409 outs <<
"4 times the number of threads are recommended when creating PreAllocatedSPA\n";
413 int32_t rowPerSplit = mA / rowSplits;
422 std::vector<std::vector<int32_t>> bSize(rowSplits, std::vector<int32_t> ( rowSplits, 0));
423 std::vector<std::vector<int32_t>> bOffset(rowSplits, std::vector<int32_t> ( rowSplits, 0));
424 std::vector<int32_t> sendSize(rowSplits);
425 double t0, t1, t2, t3, t4;
426#ifdef BENCHMARK_SPMSPV
431#pragma omp parallel for schedule(dynamic, 1)
433 for(
int b=0; b<rowSplits; b++)
436 int perBucket = veclen/rowSplits;
437 int spill = veclen%rowSplits;
438 int32_t xstart = b*perBucket + std::min(spill, b);
439 int32_t xend = (b+1)*perBucket + std::min(spill, b+1);
440 std::vector<int32_t> temp(rowSplits,0);
441 for (
int32_t i = xstart; i < xend; ++i)
444 for(
IT j=Acsc.
jc[colid]; j < Acsc.
jc[colid+1]; ++j)
448 if(rowPerSplit!=0) splitId = (rowid/rowPerSplit > rowSplits-1) ? rowSplits-1 : rowid/rowPerSplit;
454 for(
int k=0; k<rowSplits; k++)
456 bSize[b][k] = temp[k];
459 sendSize[b] = totSend;
463#ifdef BENCHMARK_SPMSPV
464 t1 = MPI_Wtime() - t0;
471 for(
int i=1; i<rowSplits; i++)
473 for(
int j=0; j<rowSplits; j++)
475 bOffset[i][j] = bOffset[i-1][j] + bSize[i-1][j];
480 std::vector<uint32_t> disp(rowSplits+1);
481 int maxBucketSize = -1;
483 for(
int j=0; j<rowSplits; j++)
485 int thisBucketSize = bOffset[rowSplits-1][j] + bSize[rowSplits-1][j];
486 disp[j+1] = disp[j] + thisBucketSize;
487 bSize[rowSplits-1][j] = 0;
488 maxBucketSize = std::max(thisBucketSize, maxBucketSize);
493#ifdef BENCHMARK_SPMSPV
494 double tseq = MPI_Wtime() - t0;
502#define L2_CACHE_SIZE 256000
504 int THREAD_BUF_LEN = 256;
505 int itemsize =
sizeof(
int32_t) +
sizeof(OVT);
508 int bufferMem = THREAD_BUF_LEN * rowSplits * itemsize + 8 * rowSplits;
512 THREAD_BUF_LEN = std::min(maxBucketSize+1,THREAD_BUF_LEN);
514#ifdef BENCHMARK_SPMSPV
523 OVT* tNumSplitA =
new OVT[rowSplits*THREAD_BUF_LEN];
524 std::vector<int32_t> tBucketSize(rowSplits);
525 std::vector<int32_t> tOffset(rowSplits);
527#pragma omp for schedule(dynamic,1)
529 for(
int b=0; b<rowSplits; b++)
532 std::fill(tBucketSize.begin(), tBucketSize.end(), 0);
533 std::fill(tOffset.begin(), tOffset.end(), 0);
534 int perBucket = veclen/rowSplits;
535 int spill = veclen%rowSplits;
536 int32_t xstart = b*perBucket + std::min(spill, b);
537 int32_t xend = (b+1)*perBucket + std::min(spill, b+1);
539 for (
int32_t i = xstart; i < xend; ++i)
542 for(
IT j=Acsc.
jc[colid]; j < Acsc.
jc[colid+1]; ++j)
544 OVT val = SR::multiply( Acsc.
num[j], numx[i]);
547 if(rowPerSplit!=0) splitId = (rowid/rowPerSplit > rowSplits-1) ? rowSplits-1 : rowid/rowPerSplit;
548 if (tBucketSize[splitId] < THREAD_BUF_LEN)
550 tIndSplitA[splitId*THREAD_BUF_LEN + tBucketSize[splitId]] = rowid;
551 tNumSplitA[splitId*THREAD_BUF_LEN + tBucketSize[splitId]++] = val;
555 std::copy(tIndSplitA + splitId*THREAD_BUF_LEN, tIndSplitA + (splitId+1)*THREAD_BUF_LEN, &SPA.
indSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]);
556 std::copy(tNumSplitA + splitId*THREAD_BUF_LEN, tNumSplitA + (splitId+1)*THREAD_BUF_LEN, &SPA.
numSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]);
557 tIndSplitA[splitId*THREAD_BUF_LEN] = rowid;
558 tNumSplitA[splitId*THREAD_BUF_LEN] = val;
559 tOffset[splitId] += THREAD_BUF_LEN ;
560 tBucketSize[splitId] = 1;
565 for(
int splitId=0; splitId<rowSplits; ++splitId)
567 if(tBucketSize[splitId]>0)
569 std::copy(tIndSplitA + splitId*THREAD_BUF_LEN, tIndSplitA + splitId*THREAD_BUF_LEN + tBucketSize[splitId], &SPA.
indSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]);
570 std::copy(tNumSplitA + splitId*THREAD_BUF_LEN, tNumSplitA + splitId*THREAD_BUF_LEN + tBucketSize[splitId], &SPA.
numSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]);
574 delete [] tIndSplitA;
575 delete [] tNumSplitA;
578#ifdef BENCHMARK_SPMSPV
579 t2 = MPI_Wtime() - t0;
582 std::vector<uint32_t> nzInRowSplits(rowSplits);
586#pragma omp parallel for schedule(dynamic,1)
588 for(
int rs=0; rs<rowSplits; ++rs)
591 for(
int i=disp[rs]; i<disp[rs+1] ; i++)
597 for(
int i=disp[rs]; i<disp[rs+1] ; i++)
600 int32_t lrowid = rowid - rs * rowPerSplit;
604 nzinds[tMergeDisp++] = rowid;
613 integerSort(nzinds + disp[rs], tMergeDisp - disp[rs]);
614 nzInRowSplits[rs] = tMergeDisp - disp[rs];
618#ifdef BENCHMARK_SPMSPV
619 t3 = MPI_Wtime() - t0;
622 std::vector<uint32_t> dispRowSplits(rowSplits+1);
623 dispRowSplits[0] = 0;
624 for(
int i=0; i<rowSplits; i++)
626 dispRowSplits[i+1] = dispRowSplits[i] + nzInRowSplits[i];
629#ifdef BENCHMARK_SPMSPV
632 int nnzy = dispRowSplits[rowSplits];
635#ifdef BENCHMARK_SPMSPV
636 tseq = MPI_Wtime() - t0;
640 int maxNnzInSplit = *std::max_element(nzInRowSplits.begin(),nzInRowSplits.end());
641 THREAD_BUF_LEN = std::min(maxNnzInSplit+1,256);
646 OVT* tnumy =
new OVT [THREAD_BUF_LEN];
650#pragma omp for schedule(dynamic,1)
652 for(
int rs=0; rs<rowSplits; rs++)
656 uint32_t * thisind = nzinds + disp[rs];
657 std::copy(nzinds+disp[rs], nzinds+disp[rs]+nzInRowSplits[rs], indy.begin()+dispRowSplits[rs]);
658 for(
int j=0; j<nzInRowSplits[rs]; j++)
661 if ( curSize < THREAD_BUF_LEN)
663 tnumy[curSize++] = SPA.
V_localy[0][thisind[j]];
667 std::copy(tnumy, tnumy+curSize, numy.begin()+dispRowSplits[rs]+tdisp);
669 tnumy[0] = SPA.
V_localy[0][thisind[j]];
675 std::copy(tnumy, tnumy+curSize, numy.begin()+dispRowSplits[rs]+tdisp);
683#ifdef BENCHMARK_SPMSPV
684 t4 = MPI_Wtime() - t0;
691#ifdef BENCHMARK_SPMSPV
692 double tall = MPI_Wtime() - tstart;
693 std::ostringstream outs1;
694 outs1 <<
"Time breakdown of SpMSpV-bucket." << std::endl;
695 outs1 <<
"Estimate buckets: "<< t1 <<
" Bucketing: " << t2 <<
" SPA-merge: " << t3 <<
" Output: " << t4 <<
" Total: "<< tall << std::endl;