Skip to content

Commit 2682562

Browse files
committed
Final (yupi :-) ) version of multistream computing on GPU
1 parent d9527e6 commit 2682562

4 files changed

Lines changed: 60 additions & 37 deletions

File tree

src/algorithm/APRConverter.hpp

Lines changed: 49 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,10 @@ class APRConverter {
7777
template <typename T>
7878
bool get_apr_cuda(APR &aAPR, PixelData<T> &input_image);
7979
template <typename T>
80-
bool get_apr_cuda_multistreams(std::vector<APR*> &aAPRs, std::vector<PixelData<T> *> &input_images, std::vector<VectorData<T> *> intensities, int numOfStreams = 3);
80+
bool get_apr_cuda_multistreams(std::vector<APR*> &aAPRs, std::vector<PixelData<T> *> &input_images, std::vector<VectorData<T> *> &intensities, int numOfStreams = 3);
81+
template<typename T>
82+
void processOnGpu(int numOfStream, int numOfStreams, int numOfImages, std::vector<PixelData<T>*> &input_images, GpuProcessingTask<ImageType> &gpt,PixelData<ImageType> &pinnedBuffer, std::vector<APR*> &aAPRs, std::vector<VectorData<T> *> &intensities);
83+
8184
#endif
8285

8386
bool verbose = true;
@@ -406,7 +409,41 @@ inline bool APRConverter<ImageType>::get_apr_cuda(APR &aAPR, PixelData<T>& input
406409
}
407410
#endif
408411

412+
409413
#ifdef APR_USE_CUDA
414+
/**
415+
* Process images on GPU
416+
*/
417+
template<typename ImageType> template<typename T>
418+
inline void APRConverter<ImageType>::processOnGpu(int numOfStream, int numOfStreams, int numOfImages, std::vector<PixelData<T>*> &input_images, GpuProcessingTask<ImageType> &gpt,PixelData<ImageType> &pinnedBuffer, std::vector<APR*> &aAPRs, std::vector<VectorData<T> *> &intensities)
419+
{
420+
// Copy to send buffer first image
421+
pinnedBuffer.copyFromMesh(*input_images[numOfStream]);
422+
423+
for (int i = numOfStream; i < numOfImages; i += numOfStreams) {
424+
std::cout << "Processing image " << i << " on stream " << numOfStream << std::endl;
425+
426+
// ---- Send image to GPU
427+
gpt.sendDataToGpu();
428+
429+
// ---- While processing image on GPU, copy next image to buffer
430+
auto future = std::async(std::launch::async, &GpuProcessingTask<ImageType>::processOnGpu, &gpt);
431+
// In the last loop we have already image copied to send buffer so skip that one
432+
if (i + numOfStreams < numOfImages) pinnedBuffer.copyFromMesh(*input_images[i + numOfStreams]);
433+
future.get();
434+
435+
// ---- Read computed data from GPU and fill APR data structure
436+
auto linearAccessGpu = gpt.getDataFromGpu();
437+
auto apr = aAPRs[i];
438+
apr->aprInfo.total_number_particles = linearAccessGpu.y_vec.size();
439+
apr->linearAccess.y_vec = std::move(linearAccessGpu.y_vec);
440+
apr->linearAccess.xz_end_vec = std::move(linearAccessGpu.xz_end_vec);
441+
apr->linearAccess.level_xz_vec = std::move(linearAccessGpu.level_xz_vec);
442+
apr->apr_initialized = true;
443+
if (intensities[i] != nullptr) *intensities[i] = std::move(linearAccessGpu.parts);
444+
}
445+
}
446+
410447
/**
411448
* Implementation of pipeline for GPU/CUDA and multiple streams
412449
* NOTE: Currently only one image is processed multiple times just get an idea how fast it can be.
@@ -417,7 +454,7 @@ inline bool APRConverter<ImageType>::get_apr_cuda(APR &aAPR, PixelData<T>& input
417454
* @param numOfStreams - number of streams to use for parallel processing on GPU
418455
*/
419456
template<typename ImageType> template<typename T>
420-
inline bool APRConverter<ImageType>::get_apr_cuda_multistreams(std::vector<APR*> &aAPRs, std::vector<PixelData<T>*> &input_images, std::vector<VectorData<T> *> intensities, int numOfStreams) {
457+
inline bool APRConverter<ImageType>::get_apr_cuda_multistreams(std::vector<APR*> &aAPRs, std::vector<PixelData<T>*> &input_images, std::vector<VectorData<T> *> &intensities, int numOfStreams) {
421458
int numOfImages = input_images.size();
422459
if (numOfImages == 0) {
423460
std::cerr << "No input images provided for APR conversion." << std::endl;
@@ -441,53 +478,30 @@ inline bool APRConverter<ImageType>::get_apr_cuda_multistreams(std::vector<APR*>
441478
pinnedBuffers.emplace_back(PixelData<T>(*input_images[i], false /* copy */, true /* pinned memory */));
442479
}
443480

444-
/////////////////////////////////
445-
/// Pipeline
446-
/////////////////////////////////
447481
APRTimer t(true);
448482

449483
// Create GpuProcessingTask for each stream and link it with pinnedBuffer
450484
std::vector<GpuProcessingTask<ImageType>> gpts;
451-
t.start_timer("Creating GPTS");
485+
t.start_timer("Creating GPTs");
452486
std::vector<std::future<void>> gpts_futures; gpts_futures.resize(numOfStreams);
453487
for (int i = 0; i < numOfStreams; ++i) {
488+
//par.noise_sd_estimate = i;
454489
gpts.emplace_back(GpuProcessingTask<ImageType>(pinnedBuffers[i], par, aAPRs[0]->level_max()));
455490
}
456491
t.stop_timer();
457492

458493
t.start_timer("GPU processing...");
459-
// Saturate all the streams with first images
494+
495+
// Start all GPTs in separate threads...
460496
for (int i = 0; i < numOfStreams; ++i) {
461-
std::cout << "Processing image " << i << " on stream " << i << std::endl;
462-
pinnedBuffers[i].copyFromMesh(*input_images[i]);
463-
gpts_futures[i] = std::async(std::launch::async, &GpuProcessingTask<ImageType>::processOnGpu, &gpts[i]);
497+
gpts_futures[i] = std::async(std::launch::async, &APRConverter<ImageType>::processOnGpu<T>, this,
498+
i, numOfStreams, numOfImages,
499+
std::ref(input_images), std::ref(gpts[i]), std::ref(pinnedBuffers[i]),
500+
std::ref(aAPRs), std::ref(intensities));
464501
}
502+
// ...and wait for all jobs to finish
503+
for (int i = 0; i < numOfStreams; ++i) gpts_futures[i].get();
465504

466-
// Main loop - get results from GPU and send new images to the streams (if any left)
467-
for (int s = 0; s < numOfImages; ++s) {
468-
int streamNum = s % numOfStreams;
469-
470-
// Get data from GpuProcessingTask - get() will block until the task is finished
471-
gpts_futures[streamNum].get();
472-
auto linearAccessGpu = gpts[streamNum].getDataFromGpu();
473-
474-
// Send next images to the stream if there are any left
475-
// We have 'numOfImages - numOfStreams' left to process after saturating the streams with first images
476-
if (s < numOfImages - numOfStreams) {
477-
int imageToProcess = s + numOfStreams;
478-
pinnedBuffers[streamNum].copyFromMesh(*input_images[imageToProcess]);
479-
std::cout << "Processing image " << imageToProcess << " on stream " << streamNum << std::endl;
480-
gpts_futures[streamNum] = std::async(std::launch::async, &GpuProcessingTask<ImageType>::processOnGpu, &gpts[streamNum]);
481-
}
482-
483-
// Fill APR data structure with data from GPU
484-
aAPRs[s]->aprInfo.total_number_particles = linearAccessGpu.y_vec.size();
485-
aAPRs[s]->linearAccess.y_vec = std::move(linearAccessGpu.y_vec);
486-
aAPRs[s]->linearAccess.xz_end_vec = std::move(linearAccessGpu.xz_end_vec);
487-
aAPRs[s]->linearAccess.level_xz_vec = std::move(linearAccessGpu.level_xz_vec);
488-
aAPRs[s]->apr_initialized = true;
489-
if (intensities[s] != nullptr) *intensities[s] = std::move(linearAccessGpu.parts);
490-
}
491505
auto allT = t.stop_timer();
492506

493507
float tpi = allT / (numOfImages);
@@ -500,7 +514,6 @@ inline bool APRConverter<ImageType>::get_apr_cuda_multistreams(std::vector<APR*>
500514
}
501515
#endif
502516

503-
504517
/**
505518
* Implementation of pipeline for CPU
506519
*

src/algorithm/ComputeGradientCuda.cu

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ public:
574574
return std::move(lacs);
575575
}
576576

577-
void processOnGpu() {
577+
void sendDataToGpu() {
578578
cudaSetDevice(cudaDevID);
579579
// Set it and copy first before copying the image
580580
// It improves *a lot* performance even though it is needed later in computeLinearStructureCuda()
@@ -583,6 +583,11 @@ public:
583583
level_xz_vec_cuda.copyH2D();
584584

585585
image.copyH2D();
586+
checkCuda(cudaStreamSynchronize(iStream));
587+
}
588+
589+
void processOnGpu() {
590+
cudaSetDevice(cudaDevID);
586591

587592
// offset image by factor (this is required if there are zero areas in the background with
588593
// uint16_t and uint8_t images, as the Bspline co-efficients otherwise may be negative!)
@@ -662,6 +667,9 @@ LinearAccessCudaStructs<ImgType> GpuProcessingTask<ImgType>::getDataFromGpu() {r
662667
template <typename ImgType>
663668
void GpuProcessingTask<ImgType>::processOnGpu() {impl->processOnGpu();}
664669

670+
template <typename ImgType>
671+
void GpuProcessingTask<ImgType>::sendDataToGpu() {impl->sendDataToGpu();}
672+
665673
// explicit instantiation of handled types
666674
template class GpuProcessingTask<uint8_t>;
667675
template class GpuProcessingTask<int>;

src/algorithm/ComputeGradientCuda.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class GpuProcessingTask {
4949

5050
LinearAccessCudaStructs<ImgType> getDataFromGpu();
5151
void processOnGpu();
52+
void sendDataToGpu();
5253

5354
void setBsplineOffset(float bspline_offset);
5455
};

test/FullPipelineCudaTest.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ namespace {
340340
// Calculate pipeline on GPU
341341
timer.start_timer(">>>>>>>>>>>>>>>>> GPU PIPELINE");
342342
GpuProcessingTask<ImageType> gpt(mGpuImage, par, maxLevel);
343+
gpt.sendDataToGpu();
343344
gpt.processOnGpu();
344345
auto linearAccessGpu = gpt.getDataFromGpu();
345346
giGpu.total_number_particles = linearAccessGpu.y_vec.size();

0 commit comments

Comments
 (0)