Я пытаюсь развернуть реализацию функции, чтобы выполнить оптимизацию в cuda. В основном у меня есть часть разделяемой памяти, которая изначально замедляла мой код, и, «развернув» мою реализацию (уменьшив общее количество потоков, и каждый поток выполнял в два раза больше работы), я смог получить существенный прирост производительности. Я хочу посмотреть, смогу ли я добиться большего прироста производительности с помощью большего количества развертываний, однако я широко использовал кортежи, чтобы это произошло. Я обнаружил, что в этом процессе происходит много дублирования кода, и я хотел бы сократить это дублирование.

Вот пример того, что часто случается в моем коде:

__device__
thrust::tuple<T,T,T,...> foo(thrust::tuple<G,G,G..> choice_arg...){
    //all do the same thing, with very similar args as well.
    T value1 = someoperation(thrust::get<0>(choice_arg),...);
    T value2 = someoperation(thrust::get<1>(choice_arg),...);
    T value3 = someoperation(thrust::get<2>(choice_arg),...);
    ...
    return thrust::make_tuple(value1, value2, value3,...);
}

Вместо того, чтобы самому писать здесь всю схему котлов, я бы хотел иметь такую ​​функцию:

__device__
thrust::tuple<T,T,T,...> foo(thrust::tuple<G,G,G..> choice_arg, ...){
    return someoperation<CHOICE_ARG_LENGTH>(choice_arg,...);
}

Я видел, как что-то вроде this может помочь, но обычный цикл шаблона не сработает, если мне нужно вернуть thrust::tuple. Это решение работало бы, если бы у thrust был thrust::tuple_cat, однако им еще предстоит объединить кортежи вариативных шаблонов, несмотря на работу, проделанную в 2014 году, и я даже не могу найти никаких разговоров, касающихся слияния реализации cat! Так можно ли реализовать поведение, которое я ищу, без реализации thust :: tuple_cat на GPU?

Обратите внимание, что я не могу использовать для этого массивы, после первоначального использования массивов я обнаружил, что получил повышение скорости на 15% бесплатно, как в визуальном профилировщике, так и в реальном применении алгоритма, который у меня был. Код очень критичен к производительности.

1
Krupip 16 Фев 2018 в 23:17

1 ответ

Лучший ответ

Если вы можете использовать CUDA 9 и c ++ 14, вы можете сделать следующее, подробности см., Например, std :: integer_sequence.

#include <iostream>
#include <utility>
#include <thrust/tuple.h>

template <typename T>
__device__ T some_operation(T a) {
  return a + 1;  // do something smart
}

template <typename T, std::size_t... I>
__device__ auto foo_impl(const T& t, std::index_sequence<I...>) {
  return thrust::make_tuple(some_operation(thrust::get<I>(t))...);
}

template <typename Tuple>
__device__ auto foo(const Tuple& t) {
  return foo_impl(t,
                  std::make_index_sequence<thrust::tuple_size<Tuple>::value>());
}

__global__ void test_kernel() {
  auto result = foo(thrust::make_tuple(3., 2, 7));
  printf("%f, %d, %d\n", thrust::get<0>(result), thrust::get<1>(result),
         thrust::get<2>(result));
}

int main() {
  test_kernel<<<1, 1>>>();
  cudaDeviceSynchronize();
}

Скомпилировать с nvcc -std=c++14 ...


Для c ++ 11

Вам нужно

  • предоставить собственную реализацию index_sequence
  • используйте завершающие возвращаемые типы.

Вот рабочая версия. Отказ от ответственности: я написал index_sequence, как мне пришло в голову. Может быть, вы хотите иметь реализацию из библиотеки std.

Вероятно, вы можете найти множество руководств по index_sequence / integer_sequence в Интернете, например на cppreference.com. Основная идея index_sequence состоит в том, что он позволяет перечислять элементы вашего кортежа (или массива). В foo создается index_sequence, который имеет 0, ..., thrust::tuple_size<Tuple>::value в качестве параметров шаблона. В foo_impl вы фиксируете эти индексы в вариационном пакете и расширяете его, вызывая some_operation для каждого из элементов кортежа.

#include <iostream>
#include <thrust/tuple.h>

namespace compat {
template <size_t... Indices>
struct index_sequence {};

namespace detail {
template <size_t N, typename Seq = index_sequence<>>
struct make_index_sequence_impl;

template <size_t N, size_t... Indices>
struct make_index_sequence_impl<N, index_sequence<Indices...>> {
  using type = typename make_index_sequence_impl<
      N - 1, index_sequence<N - 1, Indices...>>::type;
};

template <size_t... Indices>
struct make_index_sequence_impl<1, index_sequence<Indices...>> {
  using type = index_sequence<0, Indices...>;
};
}

template <size_t N>
using make_index_sequence = typename detail::make_index_sequence_impl<N>::type;
}

template <typename T>
__device__ T some_operation(T a) {
  return a + 1;  // do something smart
}

template <typename T, std::size_t... I>
__device__ auto foo_impl(const T& t, compat::index_sequence<I...>)
    -> decltype(thrust::make_tuple(some_operation(thrust::get<I>(t))...)) {
  return thrust::make_tuple(some_operation(thrust::get<I>(t))...);
}

template <typename Tuple>
__device__ auto foo(const Tuple& t) -> decltype(foo_impl(
    t, compat::make_index_sequence<thrust::tuple_size<Tuple>::value>())) {
  return foo_impl(
      t, compat::make_index_sequence<thrust::tuple_size<Tuple>::value>());
}

__global__ void test_kernel() {
  auto result = foo(thrust::make_tuple(3., 2, 7));
  printf("%f, %d, %d\n", thrust::get<0>(result), thrust::get<1>(result),
         thrust::get<2>(result));
}

int main() {
  test_kernel<<<1, 1>>>();
  cudaDeviceSynchronize();
}
1
havogt 19 Фев 2018 в 23:48