// Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #pragma once #ifndef INDEX_PARTITION_OMP_H #define INDEX_PARTITION_OMP_H #ifdef _OPENMP #include #endif #include #include // element-wise addition of two ints/tuples-of-ints template Tint element_add(Tint a, Tint b) { return a + b; } template std::tuple element_add(std::tuple a, std::tuple b) { return std::make_tuple(std::get<0>(a) + std::get<0>(b), std::get<1>(a) + std::get<1>(b)); } template std::tuple element_add(std::tuple a, std::tuple b) { return std::make_tuple(std::get<0>(a) + std::get<0>(b), std::get<1>(a) + std::get<1>(b), std::get<2>(a) + std::get<2>(b)); } // Specialized computation of index given 1-, 2-, 3- dimension length + stride template size_t compute_index(T1 length, T2 stride, size_t base) { return (length * stride) + base; } template size_t compute_index(const std::tuple& length, const std::tuple& stride, size_t base) { static_assert(std::is_integral::value, "Integral required."); static_assert(std::is_integral::value, "Integral required."); return (std::get<0>(length) * std::get<0>(stride)) + (std::get<1>(length) * std::get<1>(stride)) + base; } template size_t compute_index(const std::tuple& length, const std::tuple& stride, size_t base) { static_assert(std::is_integral::value, "Integral required."); static_assert(std::is_integral::value, "Integral required."); return (std::get<0>(length) * std::get<0>(stride)) + (std::get<1>(length) * std::get<1>(stride)) + (std::get<2>(length) * std::get<2>(stride)) + base; } // Work out how many partitions to break our iteration problem into template static size_t compute_partition_count(T1 length) { #ifdef _OPENMP // we seem to get contention from too many threads, which slows // things down. particularly noticeable with mix_3D tests static const size_t MAX_PARTITIONS = 8; size_t iters = count_iters(length); size_t hw_threads = std::min( MAX_PARTITIONS, static_cast(std::min(omp_get_num_procs(), omp_get_max_threads()))); if(!hw_threads) return 1; // don't bother threading problem sizes that are too small. pick // an arbitrary number of iterations and ensure that each thread // has at least that many iterations to process static const size_t MIN_ITERS_PER_THREAD = 2048; // either use the whole CPU, or use ceil(iters/iters_per_thread) return std::min(hw_threads, (iters + MIN_ITERS_PER_THREAD + 1) / MIN_ITERS_PER_THREAD); #else return 1; #endif } // Break a scalar length into some number of pieces, returning // [(start0, end0), (start1, end1), ...] template std::vector> partition_base(const T1& length, size_t num_parts) { static_assert(std::is_integral::value, "Integral required."); // make sure we don't exceed the length num_parts = std::min(length, num_parts); std::vector> ret(num_parts); auto partition_size = length / num_parts; T1 cur_partition = 0; for(size_t i = 0; i < num_parts; ++i, cur_partition += partition_size) { ret[i].first = cur_partition; ret[i].second = cur_partition + partition_size; } // last partition might not divide evenly, fix it up ret.back().second = length; return ret; } // Returns pairs of startindex, endindex, for 1D, 2D, 3D lengths template std::vector> partition_rowmajor(const T1& length) { return partition_base(length, compute_partition_count(length)); } // Partition on the leftmost part of the tuple, for row-major indexing template std::vector, std::tuple>> partition_rowmajor(const std::tuple& length) { auto partitions = partition_base(std::get<0>(length), compute_partition_count(length)); std::vector, std::tuple>> ret(partitions.size()); for(size_t i = 0; i < partitions.size(); ++i) { std::get<0>(ret[i].first) = partitions[i].first; std::get<1>(ret[i].first) = 0; std::get<0>(ret[i].second) = partitions[i].second; std::get<1>(ret[i].second) = std::get<1>(length); } return ret; } template std::vector, std::tuple>> partition_rowmajor(const std::tuple& length) { auto partitions = partition_base(std::get<0>(length), compute_partition_count(length)); std::vector, std::tuple>> ret(partitions.size()); for(size_t i = 0; i < partitions.size(); ++i) { std::get<0>(ret[i].first) = partitions[i].first; std::get<1>(ret[i].first) = 0; std::get<2>(ret[i].first) = 0; std::get<0>(ret[i].second) = partitions[i].second; std::get<1>(ret[i].second) = std::get<1>(length); std::get<2>(ret[i].second) = std::get<2>(length); } return ret; } #endif