【问题标题】:Extracting the simd-vector length from a template parameter to use for a local type从模板参数中提取 simd 向量长度以用于本地类型
【发布时间】:2021-04-09 19:19:24
【问题描述】:

我正在努力寻找正确的 C++/clang 语句来解决以下问题。首先请注意,由于在算术发生之前整数提升,以下内容不会溢出 unsigned short。

unsigned short testme = 16320;
testme = testme * 257 / 64;

结果是 65535。但是当我使用 simd 在无符号短裤的向量上尝试类似的东西时,它不起作用:

#import <simd/simd.h>

template <typename T>
  void muldiv( T* data, unsigned multiply, unsigned divide)
{
    *data = (*data * multiply) / divide;
}

...

simd::ushort4 testme = 16320;
muldiv( &testme, 257, 64);

这给出了四个 1023 的向量。没有发生整数提升和乘法包装。在查看了 clang 文档之后,我能想到的最好的就是这个。请注意,调用者必须提供一个虚拟参数,以提供工作精度作为模板类型参数。

#import <simd/simd.h>

template <typename T, typename W>
 void muldiv( T* data, unsigned multiply, unsigned divide, W workingtype)
{
    *data = __builtin_convertvector( (__builtin_convertvector(*data, W) * multiply) / divide, T);
}

...

simd::ushort4 testme = 16320;
muldiv( &testme, 257, 64, simd::uint4());

现在我得到一个包含四个 65535 的向量。 T 是模板参数的原因是有时我会传递 ushort4、ushort8、ushort16 等。但我发现将工作精度作为参数传递是很难看的,因为它始终是无符号整数。我想不出一种从 T 中提取 simd-length 的方法,因此我可以在本地声明类型 W 。函数中这样的东西会很好:

typedef unsigned int W __attribute__((__vector_size__( ?? )));

但我不知道如何让它发挥作用。我尝试过这样的事情:

bool hopeful = __is_convertible_to( simd::ushort4, simd::uint4);

但希望总是返回 false。

谁能告诉我我需要什么魔法?

注意这是在提供 的 Apple 平台上。

【问题讨论】:

  • simd 对不同的向量类型没有任何特征吗?如果没有,那么也许你可以自己动手?
  • 这是金属namespace simd吗?
  • 您是否尝试编写一个匹配 __attribute__((__ext_vector_type__(3)))__attribute__((__ext_vector_type__(2))) 等的特征并将其复制到新类型?
  • 一个简单的sizeof?
  • 请提供minimal reproducible example。你的simd::uint4 是在哪里定义的?另外,我认为通常向上转换不是最有效的解决方案。而不是testme * 257 / 64 我会计算(testme &lt;&lt; 2) + (testme &gt;&gt; 6)

标签: c++ templates clang simd metal


【解决方案1】:

因此,clang 允许您对属性进行模式匹配并在模板中生成新的属性修改类型。

所以我们可以这样做。

simd 宽度属性上的第一个模式匹配:

template<class T>
struct get_simd_width;

template<class T, std::size_t x>
struct get_simd_width< __attribute__((__ext_vector_type__(x))) T >:
  std::integral_constant<std::size_t, x>
{};

另外,提取属性类型的基础类型:

template<class T>
struct get_simd_type;

template<class T, std::size_t x>
struct get_simd_type< __attribute__((__ext_vector_type__(x))) T >
{
  using type = T;
};

然后我们做一些语法糖来让它们更容易使用:

template<class T>
constexpr std::size_t simd_width = get_simd_width<T>{};
template<class T>
using simd_type = typename get_simd_type<T>::type;

这是用一个属性生成一个新的simd类型:

template<class T>
struct simd_helper;
template<class T, std::size_t N>
struct simd_helper<T[N]> {
  using type = __attribute__((__ext_vector_type__(N))) T;
};
template<class T>
using simd = typename simd_helper<T>::type;

然后simd&lt;int[4]&gt; 生成宽度为 4 的 simd 类型。

这些应该可以解决您的问题。 Live example.

template <class T>
void muldiv( T* data, unsigned multiply, unsigned divide)
{
   using W = simd<int[simd_width<T>]>;
   *data = __builtin_convertvector( (__builtin_convertvector(*data, W) * multiply) / divide, T);
}

【讨论】:

  • 哇,这可行,但我还不明白。在我弄清楚时标记正确。谢谢!
  • @Mustang 我检查了,你可以在 clang: 的 simd 属性上键入 match:所以我做了并提取了它们的值。同样,您可以使用模板来创建属性修改类型。
  • @Mustang 所以我添加了每种类型的描述,并删除了糟糕的simd 一个为好的。
  • @Adam 感谢您的补充说明。对于掌握这样的元编程的最佳文本有什么建议吗?
  • @Mustang 几乎任何质量的非介绍性 C++ 书籍? reddit.com/r/cpp/comments/by60wx/modern_c_authors_and_books 应该不错
猜你喜欢
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-20
  • 2014-10-31
  • 2013-01-24
相关资源
最近更新 更多