【问题标题】:Can't convert value to a vector with Intel Intrinsics无法使用 Intel Intrinsics 将值转换为向量
【发布时间】:2021-10-23 08:00:46
【问题描述】:

我正在使用 Intel Intrinsics 并遇到这个奇怪的错误。

src/header/header.c:18:3: error: can’t convert value to a vector
       18 |   int has_value = (int)_mm_cmpestrc(buffer, 4, u_str.vec, 4,
          |   ^~~

我尝试了以下没有(int) 演员,我也尝试了<nmmintrin.h>

#include "./header.h"
#ifdef __SIMD__
#include <x86intrin.h>
#endif

static inline void parse_with_simd(const char *buffer, const int buffer_len) {
  union {
    __m128i vec;
    char * str;
  } u_str = {.str = "GET "};
  int has_value = (int)_mm_cmpestrc(buffer, 4, u_str.vec, 4,
                               _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_EACH); // <-- this line

我的 CPPFLAGS 和 CFLAGS

CFLAGS = -Wall -O0 -std=c11 -g
CPPFLAGS = -DDEBUG -D__SIMD__

当我查看 _mm_cmpstrc 的定义时,它显示返回类型也是一个 int!

#define _mm_cmpestrc(A, LA, B, LB, M) \
  (int)__builtin_ia32_pcmpestric128((__v16qi)(__m128i)(A), (int)(LA), \
                                    (__v16qi)(__m128i)(B), (int)(LB), \
                                    (int)(M))

【问题讨论】:

  • _mm_cmpestrc 调用的第一个参数是char* - 这与the documentation 中指定的类型非常不同,它是__m128i .另请注意,宏扩展中的错误消息可能指向奇怪的地方。
  • ... 使用 MSVC(和 nmmintrin.h 标头),我得到这个:error C2440: 'function': cannot convert from 'const char *' to '__m128i'我>
  • @AdrianMole 我添加了来自(__m128i)buffer 的演员表和同样的问题。我的理解是它们都是指向字符串数据的指针。
  • __m128i 类型(很可能)由 C 编译器实现为 structunion。您不能将 char* 转换为 struct。不确定您要做什么,因此无法提供太多帮助。
  • @AdrianMole,也许应该改成char str[sizeof(__m128i)]_mm_cmpestrc 文档说字符串必须嵌入到向量中,而不是指向字符串的指针。

标签: c gcc x86 sse intrinsics


【解决方案1】:

该指令要求将字符串的内容放入向量中。不是指向字符串的指针。使用memcpy 可能是实现它的最简单方法。


static inline void parse_with_simd(const char *buffer, const int buffer_len) {
  __m128i a, b;
  // requires buffer_len be at most 16
  memcpy(&a, buffer, buffer_len);
  memcpy(&b, "GET ", 5);
  int has_value = _mm_cmpestrc(a, 4, b, 4, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_EACH);
  ...
}

【讨论】:

  • 可变大小的 memcpy 可能会使 a 中的某些部分未初始化,这可能很慢,而且似乎是一个糟糕的选择。如果您分配buffer 使其成为guaranteed not to be within 16 bytes of an unmapped page,则__m128i a = _mm_loadu_si128( (const __m128i*)buffer ) 将是标准方式。 _mm_cmpestr* 需要一个长度参数来忽略超过该长度的垃圾。 (虽然它比在数据中查找 0 字节的 pcmpistri 慢,对于隐式长度的 C 字符串)。
  • 同样,b = _mm_setr_epi8('G', 'E', 'T', ' ', 0,0,0,...) 将是初始化该操作数的正常方式。 (或者来自字符串文字或字符数组的_mm_loadu_si32 可以工作,甚至来自uint32_t_mm_cvtsi32_si128 具有正确的小端十六进制值。)。
  • @Peter Cordes 是的,但 OP 对缓冲区的长度几乎没有任何限制。也没有详细说明“_mm_cmpestrc”之后做了什么。加载整个向量可能会出现段错误。使用 memcpy 是一个更安全的选择
  • 如果首先将向量对象归零,则只有在 C UB 方面完全安全,否则它可能会部分未初始化,但逻辑上_mm_cmpestrc 会读取整个__m128i。是的,正如我所说,为了能够做有效的事情(恰好加载 16 个字节,而不是“整个缓冲区”),您需要确保调用者永远不会在页面末尾附近传递一个指针。使用 SIMD 的全部意义在于性能,因此,如果您在使用它的过程中朝自己的脚开枪,那么您就达不到目的了。
  • 我认为这里的问题是我的实现,以及两个缓冲区的类型都不正确。从技术上讲,缓冲区可以是任何大小。我不确定如何回答这个问题,因为这回答了这个问题。我已经修改了我的实现,现在它包含一个错误而不是没有编译,所以我会用赞成票、没有答案来标记它,并提出一个新问题。
猜你喜欢
  • 1970-01-01
  • 2017-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多