| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 418e19b commit f729e5f
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -47,13 +47,15 @@ To use %Taskflow v3.1.0, you need a compiler that supports C++17: | |||
| 47 | 47 | @subsection release-3-1-0_cudaflow cudaFlow | |
| 48 | 48 | ||
| 49 | 49 | @li optimized tf::cudaRoundRobinCapturing through an event-pruning heuristic | |
| 50 | + @li optimized the default block size used in %cudaFlow algorithms | ||
| 50 | 51 | @li added tf::cudaFlow::clear() to clean up a %cudaFlow | |
| 51 | 52 | @li added tf::cudaFlow::num_tasks() to query the task count in a %cudaFlow | |
| 52 | 53 | @li added tf::cudaTask::num_dependents() to query the dependent count in a %cudaTask | |
| 53 | 54 | @li added tf::cudaFlowCapturer::clear() to clean up a %cudaFlow capturer | |
| 54 | 55 | @li added tf::cudaFlowCapturer::num_tasks() to query the task count in a %cudaFlow capturer | |
| 55 | 56 | @li added %cudaFlow examples | |
| 56 | 57 | + parallel reduction (examples/cuda/cuda_reduce.cu) | |
| 58 | + + parallel transform (examples/cuda/cuda_transform.cu) | ||
| 57 | 59 | ||
| 58 | 60 | @subsection release-3-1-0_syclflow syclFlow | |
| 59 | 61 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -9,6 +9,7 @@ list(APPEND TF_CUDA_EXAMPLES | |||
| 9 | 9 | cuda_knn | |
| 10 | 10 | cuda_capturer | |
| 11 | 11 | cuda_reduce | |
| 12 | + cuda_transform | ||
| 12 | 13 | ) | |
| 13 | 14 | ||
| 14 | 15 | foreach(cuda_example IN LISTS TF_CUDA_EXAMPLES) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,50 @@ | |||
| 1 | + // This program demonstrates how to performs a parallel transform | ||
| 2 | + // using cudaFlow. | ||
| 3 | + | ||
| 4 | + #include <taskflow/cudaflow.hpp> | ||
| 5 | + | ||
| 6 | + int main(int argc, char* argv[]) { | ||
| 7 | + | ||
| 8 | + if(argc != 2) { | ||
| 9 | + std::cerr << "usage: ./cuda_transform num_items\n"; | ||
| 10 | + std::exit(EXIT_FAILURE); | ||
| 11 | + } | ||
| 12 | + | ||
| 13 | + size_t N = std::atoi(argv[1]); | ||
| 14 | + | ||
| 15 | + auto data = tf::cuda_malloc_shared<int>(N); | ||
| 16 | + auto src1 = tf::cuda_malloc_shared<int>(N); | ||
| 17 | + auto src2 = tf::cuda_malloc_shared<int>(N); | ||
| 18 | + auto src3 = tf::cuda_malloc_shared<int>(N); | ||
| 19 | + | ||
| 20 | + // initialize the data | ||
| 21 | + for(size_t i=0; i<N; i++) { | ||
| 22 | + data[i] = 0; | ||
| 23 | + src1[i] = 1; | ||
| 24 | + src2[i] = 2; | ||
| 25 | + src3[i] = 3; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + // perform parallel transform | ||
| 29 | + tf::cudaFlow cudaflow; | ||
| 30 | + | ||
| 31 | + // data[i] = src1[i] + src2[i] + src3[i] | ||
| 32 | + cudaflow.transform( | ||
| 33 | + data, data+N, | ||
| 34 | + [] __device__ (int a, int b, int c) { return a+b+c; }, | ||
| 35 | + src1, src2, src3 | ||
| 36 | + ); | ||
| 37 | + | ||
| 38 | + cudaflow.offload(); | ||
| 39 | + | ||
| 40 | + // inspect the result | ||
| 41 | + for(size_t i=0; i<N; i++) { | ||
| 42 | + if(data[i] != (src1[i] + src2[i] + src3[i])) { | ||
| 43 | + throw std::runtime_error("incorrect result"); | ||
| 44 | + } | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + std::cout << "correct result\n"; | ||
| 48 | + | ||
| 49 | + return 0; | ||
| 50 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,23 +13,6 @@ | |||
| 13 | 13 | ||
| 14 | 14 | namespace tf { | |
| 15 | 15 | ||
| 16 | - /** | ||
| 17 | - @brief queries the maximum threads allowed per block | ||
| 18 | - */ | ||
| 19 | - constexpr size_t cuda_default_max_threads_per_block() { | ||
| 20 | - return 512; | ||
| 21 | - } | ||
| 22 | - | ||
| 23 | - /** | ||
| 24 | - @brief queries the default number of threads per block to run N elements | ||
| 25 | - */ | ||
| 26 | - constexpr size_t cuda_default_block_size(size_t N) { | ||
| 27 | - if(N <= 32) return 32; | ||
| 28 | - else { | ||
| 29 | - return std::min(cuda_default_max_threads_per_block(), next_pow2(N)); | ||
| 30 | - } | ||
| 31 | - } | ||
| 32 | - | ||
| 33 | 16 | // ---------------------------------------------------------------------------- | |
| 34 | 17 | // class definition: cudaFlowCapturerBase | |
| 35 | 18 | // ---------------------------------------------------------------------------- | |
@@ -511,6 +494,8 @@ class cudaFlowCapturer { | |||
| 511 | 494 | void offload(); | |
| 512 | 495 | ||
| 513 | 496 | private: | |
| 497 | + | ||
| 498 | + const size_t _MAX_BLOCK_SIZE; | ||
| 514 | 499 | ||
| 515 | 500 | handle_t _handle; | |
| 516 | 501 | ||
@@ -527,18 +512,26 @@ class cudaFlowCapturer { | |||
| 527 | 512 | cudaGraph_t _capture(); | |
| 528 | 513 | ||
| 529 | 514 | void _destroy_executable(); | |
| 515 | + | ||
| 516 | + size_t _default_block_size(size_t) const; | ||
| 530 | 517 | }; | |
| 531 | 518 | ||
| 532 | 519 | // constructs a cudaFlow capturer from a taskflow | |
| 533 | 520 | inline cudaFlowCapturer::cudaFlowCapturer(cudaGraph& g) : | |
| 534 | - _handle{std::in_place_type_t<Internal>{}}, | ||
| 535 | - _graph {g} { | ||
| 521 | + _MAX_BLOCK_SIZE { | ||
| 522 | + cuda_get_device_max_threads_per_block(cuda_get_device()) | ||
| 523 | + }, | ||
| 524 | + _handle {std::in_place_type_t<Internal>{}}, | ||
| 525 | + _graph {g} { | ||
| 536 | 526 | } | |
| 537 | 527 | ||
| 538 | 528 | // constructs a standalone cudaFlow capturer | |
| 539 | 529 | inline cudaFlowCapturer::cudaFlowCapturer() : | |
| 540 | - _handle{std::in_place_type_t<External>{}}, | ||
| 541 | - _graph {std::get<External>(_handle).graph} { | ||
| 530 | + _MAX_BLOCK_SIZE { | ||
| 531 | + cuda_get_device_max_threads_per_block(cuda_get_device()) | ||
| 532 | + }, | ||
| 533 | + _handle {std::in_place_type_t<External>{}}, | ||
| 534 | + _graph {std::get<External>(_handle).graph} { | ||
| 542 | 535 | } | |
| 543 | 536 | ||
| 544 | 537 | inline cudaFlowCapturer::~cudaFlowCapturer() { | |
@@ -547,6 +540,11 @@ inline cudaFlowCapturer::~cudaFlowCapturer() { | |||
| 547 | 540 | } | |
| 548 | 541 | } | |
| 549 | 542 | ||
| 543 | + // Function: _default_block_size | ||
| 544 | + inline size_t cudaFlowCapturer::_default_block_size(size_t N) const { | ||
| 545 | + return N <= 32u ? 32u : std::min(_MAX_BLOCK_SIZE, next_pow2(N)); | ||
| 546 | + } | ||
| 547 | + | ||
| 550 | 548 | // Function: empty | |
| 551 | 549 | inline bool cudaFlowCapturer::empty() const { | |
| 552 | 550 | return _graph.empty(); | |
@@ -642,10 +640,10 @@ cudaTask cudaFlowCapturer::single_task(C&& callable) { | |||
| 642 | 640 | // Function: for_each | |
| 643 | 641 | template <typename I, typename C> | |
| 644 | 642 | cudaTask cudaFlowCapturer::for_each(I first, I last, C&& c) { | |
| 645 | - return on([first, last, c=std::forward<C>(c)](cudaStream_t stream) mutable { | ||
| 643 | + return on([&, first, last, c=std::forward<C>(c)](cudaStream_t stream) mutable { | ||
| 646 | 644 | // TODO: special case for N == 0? | |
| 647 | 645 | size_t N = std::distance(first, last); | |
| 648 | - size_t B = cuda_default_block_size(N); | ||
| 646 | + size_t B = _default_block_size(N); | ||
| 649 | 647 | cuda_for_each<I, C><<<(N+B-1)/B, B, 0, stream>>>(first, N, c); | |
| 650 | 648 | }); | |
| 651 | 649 | } | |
@@ -658,22 +656,22 @@ cudaTask cudaFlowCapturer::for_each_index(I beg, I end, I inc, C&& c) { | |||
| 658 | 656 | TF_THROW("invalid range [", beg, ", ", end, ") with inc size ", inc); | |
| 659 | 657 | } | |
| 660 | 658 | ||
| 661 | - return on([beg, end, inc, c=std::forward<C>(c)] (cudaStream_t stream) mutable { | ||
| 659 | + return on([&, beg, end, inc, c=std::forward<C>(c)] (cudaStream_t stream) mutable { | ||
| 662 | 660 | // TODO: special case when N is 0? | |
| 663 | 661 | size_t N = distance(beg, end, inc); | |
| 664 | - size_t B = cuda_default_block_size(N); | ||
| 662 | + size_t B = _default_block_size(N); | ||
| 665 | 663 | cuda_for_each_index<I, C><<<(N+B-1)/B, B, 0, stream>>>(beg, inc, N, c); | |
| 666 | 664 | }); | |
| 667 | 665 | } | |
| 668 | 666 | ||
| 669 | 667 | // Function: transform | |
| 670 | 668 | template <typename I, typename C, typename... S> | |
| 671 | 669 | cudaTask cudaFlowCapturer::transform(I first, I last, C&& c, S... srcs) { | |
| 672 | - return on([first, last, c=std::forward<C>(c), srcs...] | ||
| 670 | + return on([&, first, last, c=std::forward<C>(c), srcs...] | ||
| 673 | 671 | (cudaStream_t stream) mutable { | |
| 674 | 672 | // TODO: special case when N is 0? | |
| 675 | 673 | size_t N = std::distance(first, last); | |
| 676 | - size_t B = cuda_default_block_size(N); | ||
| 674 | + size_t B = _default_block_size(N); | ||
| 677 | 675 | cuda_transform<I, C, S...><<<(N+B-1)/B, B, 0, stream>>>(first, N, c, srcs...); | |
| 678 | 676 | }); | |
| 679 | 677 | } | |
@@ -682,13 +680,13 @@ cudaTask cudaFlowCapturer::transform(I first, I last, C&& c, S... srcs) { | |||
| 682 | 680 | template <typename I, typename T, typename C> | |
| 683 | 681 | cudaTask cudaFlowCapturer::reduce(I first, I last, T* result, C&& c) { | |
| 684 | 682 | ||
| 685 | - return on([first, last, result, c=std::forward<C>(c)] | ||
| 683 | + return on([&, first, last, result, c=std::forward<C>(c)] | ||
| 686 | 684 | (cudaStream_t stream) mutable { | |
| 687 | 685 | //using value_t = std::decay_t<decltype(*std::declval<I>())>; | |
| 688 | 686 | ||
| 689 | 687 | // TODO: special case N == 0? | |
| 690 | 688 | size_t N = std::distance(first, last); | |
| 691 | - size_t B = cuda_default_block_size(N); | ||
| 689 | + size_t B = _default_block_size(N); | ||
| 692 | 690 | ||
| 693 | 691 | cuda_reduce<I, T, C, false><<<1, B, B*sizeof(T), stream>>>( | |
| 694 | 692 | first, N, result, c | |
@@ -702,13 +700,13 @@ cudaTask cudaFlowCapturer::uninitialized_reduce( | |||
| 702 | 700 | I first, I last, T* result, C&& c | |
| 703 | 701 | ) { | |
| 704 | 702 | ||
| 705 | - return on([first, last, result, c=std::forward<C>(c)] | ||
| 703 | + return on([&, first, last, result, c=std::forward<C>(c)] | ||
| 706 | 704 | (cudaStream_t stream) mutable { | |
| 707 | 705 | //using value_t = std::decay_t<decltype(*std::declval<I>())>; | |
| 708 | 706 | ||
| 709 | 707 | // TODO: special case N == 0? | |
| 710 | 708 | size_t N = std::distance(first, last); | |
| 711 | - size_t B = cuda_default_block_size(N); | ||
| 709 | + size_t B = _default_block_size(N); | ||
| 712 | 710 | ||
| 713 | 711 | cuda_reduce<I, T, C, true><<<1, B, B*sizeof(T), stream>>>( | |
| 714 | 712 | first, N, result, c | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -567,17 +567,24 @@ class cudaFlow { | |||
| 567 | 567 | ||
| 568 | 568 | private: | |
| 569 | 569 | ||
| 570 | + const size_t _MAX_BLOCK_SIZE; | ||
| 571 | + | ||
| 570 | 572 | handle_t _handle; | |
| 571 | 573 | ||
| 572 | 574 | cudaGraph& _graph; | |
| 573 | 575 | ||
| 574 | 576 | cudaGraphExec_t _executable {nullptr}; | |
| 575 | 577 | ||
| 576 | 578 | cudaFlow(cudaGraph&); | |
| 579 | + | ||
| 580 | + size_t _default_block_size(size_t N) const; | ||
| 577 | 581 | }; | |
| 578 | 582 | ||
| 579 | 583 | // Construct a standalone cudaFlow | |
| 580 | 584 | inline cudaFlow::cudaFlow() : | |
| 585 | + _MAX_BLOCK_SIZE { | ||
| 586 | + cuda_get_device_max_threads_per_block(cuda_get_device()) | ||
| 587 | + }, | ||
| 581 | 588 | _handle {std::in_place_type_t<External>{}}, | |
| 582 | 589 | _graph {std::get<External>(_handle).graph} { | |
| 583 | 590 | ||
@@ -589,6 +596,9 @@ inline cudaFlow::cudaFlow() : | |||
| 589 | 596 | ||
| 590 | 597 | // Construct the cudaFlow from executor (internal graph) | |
| 591 | 598 | inline cudaFlow::cudaFlow(cudaGraph& g) : | |
| 599 | + _MAX_BLOCK_SIZE { | ||
| 600 | + cuda_get_device_max_threads_per_block(cuda_get_device()) | ||
| 601 | + }, | ||
| 592 | 602 | _handle {std::in_place_type_t<Internal>{}}, | |
| 593 | 603 | _graph {g} { | |
| 594 | 604 | ||
@@ -609,6 +619,11 @@ inline cudaFlow::~cudaFlow() { | |||
| 609 | 619 | _graph._native_handle = nullptr; | |
| 610 | 620 | } | |
| 611 | 621 | ||
| 622 | + // Function: _default_block_size | ||
| 623 | + inline size_t cudaFlow::_default_block_size(size_t N) const { | ||
| 624 | + return N <= 32u ? 32u : std::min(_MAX_BLOCK_SIZE, next_pow2(N)); | ||
| 625 | + } | ||
| 626 | + | ||
| 612 | 627 | // Procedure: clear | |
| 613 | 628 | inline void cudaFlow::clear() { | |
| 614 | 629 | ||
@@ -1017,7 +1032,7 @@ template <typename I, typename C> | |||
| 1017 | 1032 | cudaTask cudaFlow::for_each(I first, I last, C&& c) { | |
| 1018 | 1033 | ||
| 1019 | 1034 | size_t N = std::distance(first, last); | |
| 1020 | - size_t B = cuda_default_block_size(N); | ||
| 1035 | + size_t B = _default_block_size(N); | ||
| 1021 | 1036 | ||
| 1022 | 1037 | // TODO: special case when N is 0? | |
| 1023 | 1038 | ||
@@ -1037,7 +1052,7 @@ cudaTask cudaFlow::for_each_index(I beg, I end, I inc, C&& c) { | |||
| 1037 | 1052 | // TODO: special case when N is 0? | |
| 1038 | 1053 | ||
| 1039 | 1054 | size_t N = distance(beg, end, inc); | |
| 1040 | - size_t B = cuda_default_block_size(N); | ||
| 1055 | + size_t B = _default_block_size(N); | ||
| 1041 | 1056 | ||
| 1042 | 1057 | return kernel( | |
| 1043 | 1058 | (N+B-1) / B, B, 0, cuda_for_each_index<I, C>, beg, inc, N, std::forward<C>(c) | |
@@ -1051,7 +1066,7 @@ cudaTask cudaFlow::transform(I first, I last, C&& c, S... srcs) { | |||
| 1051 | 1066 | // TODO: special case when N is 0? | |
| 1052 | 1067 | ||
| 1053 | 1068 | size_t N = std::distance(first, last); | |
| 1054 | - size_t B = cuda_default_block_size(N); | ||
| 1069 | + size_t B = _default_block_size(N); | ||
| 1055 | 1070 | ||
| 1056 | 1071 | return kernel( | |
| 1057 | 1072 | (N+B-1) / B, B, 0, cuda_transform<I, C, S...>, | |
@@ -1067,7 +1082,7 @@ cudaTask cudaFlow::reduce(I first, I last, T* result, C&& op) { | |||
| 1067 | 1082 | ||
| 1068 | 1083 | // TODO: special case N == 0? | |
| 1069 | 1084 | size_t N = std::distance(first, last); | |
| 1070 | - size_t B = cuda_default_block_size(N); | ||
| 1085 | + size_t B = _default_block_size(N); | ||
| 1071 | 1086 | ||
| 1072 | 1087 | return kernel( | |
| 1073 | 1088 | 1, B, B*sizeof(T), cuda_reduce<I, T, C, false>, | |
@@ -1083,7 +1098,7 @@ cudaTask cudaFlow::uninitialized_reduce(I first, I last, T* result, C&& op) { | |||
| 1083 | 1098 | ||
| 1084 | 1099 | // TODO: special case N == 0? | |
| 1085 | 1100 | size_t N = std::distance(first, last); | |
| 1086 | - size_t B = cuda_default_block_size(N); | ||
| 1101 | + size_t B = _default_block_size(N); | ||
| 1087 | 1102 | ||
| 1088 | 1103 | return kernel( | |
| 1089 | 1104 | 1, B, B*sizeof(T), cuda_reduce<I, T, C, true>, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -378,12 +378,7 @@ inline syclFlow::syclFlow(Executor& e, syclGraph& g, sycl::queue& queue) : | |||
| 378 | 378 | ||
| 379 | 379 | // Function: _default_group_size | |
| 380 | 380 | inline size_t syclFlow::_default_group_size(size_t N) const { | |
| 381 | - if(N <= 32) { | ||
| 382 | - return 32; | ||
| 383 | - } | ||
| 384 | - else { | ||
| 385 | - return std::min(_MAX_WORK_GROUP_SIZE, next_pow2(N)); | ||
| 386 | - } | ||
| 381 | + return N <= 32u ? 32u : std::min(_MAX_WORK_GROUP_SIZE, next_pow2(N)); | ||
| 387 | 382 | } | |
| 388 | 383 | ||
| 389 | 384 | // Function: empty | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -465,15 +465,6 @@ TEST_CASE("capture_for_each_index.double" * doctest::timeout(300)) { | |||
| 465 | 465 | // transform | |
| 466 | 466 | // ---------------------------------------------------------------------------- | |
| 467 | 467 | ||
| 468 | - struct TransformFunc { | ||
| 469 | - __device__ int operator () (int& v1, float& v2, double& v3) { | ||
| 470 | - v1 = 1; | ||
| 471 | - v2 = 3.0f; | ||
| 472 | - v3 = 5.0; | ||
| 473 | - return 17; | ||
| 474 | - } | ||
| 475 | - }; | ||
| 476 | - | ||
| 477 | 468 | template <typename F> | |
| 478 | 469 | void transform() { | |
| 479 | 470 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments