很抱歉,如果之前有人问过,但我找不到这个确切的问题。
我有一个模板化的CUDA内核,如下所示:
template<int firstTextureIndex, int secondTextureIndex, int thirdTextureIndex> __global__ void myKernel
三种纹理索引模板类型的范围为0-7,直到运行时才会知道。我需要实例化该内核的所有512个组合,然后根据纹理索引的运行时值调用正确的模板。
我从来没有写过任何预处理宏,我试图避免它。另一篇文章here展示了如何通过这样做递归地为一个模板变量实例化许多类模板:
template<int i>
class loop {
loop<i-1> x;
}
template<>
class loop<1> {
}
loop<10> l;
我正努力将其扩展到3个变量和一个函数(而不是类)来解决我的问题。即使我弄清楚如何以这种方式实例化所有这些,如何在没有嵌套的switch语句的情况下在运行时实际调用512种可能性中的1种?为了说明,我试图避免的嵌套switch语句如下:
switch(firstTextureIndex)
{
case 0:
switch(secondTextureIndex)
{
case 1:
switch(thirdTextureIndex)
{
case 2:
myKernel<0, 1, 2><<<grid, block>>>(param1, param2, param3);
break;
}
break;
}
break;
}
如果我弄清楚如何为所有这些实例化0-7,我可以称之为:
myKernel<i, j, k><<<grid, block>>>(param1, param2);
如果我使i,j和k枚举类型只包含0-7?这样编译器就可以知道所有可能的值,因为我将它们全部实例化它就可以了吗?
请注意,这个三重模板有很好的理由传递纹理索引,但我省略了简洁的解释。任何有关实例化和/或调用此内核的帮助都将非常感激。
编辑:Jarod42提供了一个完全符合我要求的有效解决方案。不幸的是,我现在意识到c ++标准在这里很重要。我正在使用c ++ 98/03和最新稳定版本的boost库,所以使用这些解决方案是理想的。我可能会使用c ++ 11,但由于编译器的限制,c ++ 14已经出局了。
答案 0 :(得分:3)
您可以执行以下操作:
template <std::size_t I>
void do_job()
{
myKernel<I / 64, (I / 8) % 8, I % 8>{}();
}
template <std::size_t ... Is>
void callMyKernel(std::index_sequence<Is...>, std::size_t i, std::size_t j, std::size_t k)
{
std::function<void()> fs[] = {&do_job<Is>...};
fs[i * 64 + j * 8 + k]();
}
void callMyKernel(std::size_t i, std::size_t j, std::size_t k)
{
callMyKernel(std::make_index_sequence<512>{}, i, j, k);
}
答案 1 :(得分:2)
以下代码是 C ++ 98/03 和 boost.MPL 的实现。肯定有改进的余地(例如隐藏全局指针数组,检查非法组合,......)。
这个想法是递归地遍历整数列表的所有组合,从而为每个组合填充一个函数指针数组。
我之前使用过类似的,更复杂的代码,在运行时选择内核参数(自动调整)的最佳组合,如launch_bounds
和其他选项:culgt/runtimechooser。
以下是您案例的简化版
#include <iostream>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/vector_c.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/at.hpp>
namespace mpl = boost::mpl;
template<int index1, int index2, int index3> void execKernel()
{
std::cout << "Kernel called with " << index1 << "/" << index2 << "/" << index3 << std::endl;
}
typedef void (*FPTR)();
FPTR ptr[512];
struct NIL
{
public:
static const int value = 0;
};
template<typename Seq, typename T1, typename T2 = NIL> class MakeSequenceImpl
{
public:
template<typename T> void operator()(T)
{
typedef MakeSequenceImpl<typename mpl::push_back<Seq,T>::type,T2> RunSeq;
mpl::for_each<T1>( RunSeq() );
}
};
template<typename Seq> class MakeSequenceImpl<Seq, NIL, NIL>
{
public:
template<typename T> void operator()(T)
{
typedef typename mpl::push_back<Seq,T>::type FinalSeq;
int index = mpl::at<FinalSeq,mpl::int_<0> >::type::value * 64
+ mpl::at<FinalSeq,mpl::int_<1> >::type::value * 8
+ mpl::at<FinalSeq,mpl::int_<2> >::type::value;
ptr[index] = execKernel<mpl::at<FinalSeq,mpl::int_<0> >::type::value, mpl::at<FinalSeq,mpl::int_<1> >::type::value, mpl::at<FinalSeq,mpl::int_<2> >::type::value>;
}
};
template<typename T0, typename T1, typename T2> class MakeSequence
{
public:
typedef mpl::vector_c<int> Seq;
MakeSequence()
{
typedef MakeSequenceImpl<Seq, T1, T2> RunSeq;
mpl::for_each<T0>( RunSeq() );
}
};
void callWrapper( int i, int j, int k )
{
ptr[i*64+j*8+k]();
}
typedef mpl::vector_c< int, 0, 1, 2, 3, 4, 5, 6, 7 > list1;
typedef mpl::vector_c< int, 0, 1, 2, 3, 4, 5, 6, 7 > list2;
typedef mpl::vector_c< int, 0, 1, 2, 3, 4, 5, 6, 7 > list3;
int main()
{
MakeSequence<list1,list2,list3> frontend;
int i,j,k;
std::cin >> i;
std::cin >> j;
std::cin >> k;
callWrapper(i,j,k);
}