Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/xpath/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用GCC向量扩展存储、修改和检索字符串?_C_Gcc_Vectorization_Simd - Fatal编程技术网

使用GCC向量扩展存储、修改和检索字符串?

使用GCC向量扩展存储、修改和检索字符串?,c,gcc,vectorization,simd,C,Gcc,Vectorization,Simd,提供了SIMD指令的抽象 我想知道如何将它们用于字符串处理,例如屏蔽缓冲区的每个字节: typedef uint8_t v32ui __attribute__ ((vector_size(32))); void f(const uint8_t *begin, const uint8_t *end, uint8_t *o) { for (; begin < end; begin += 32, o+=32) *(v32ui*) o = (*(v32ui*) begin)

提供了SIMD指令的抽象

我想知道如何将它们用于字符串处理,例如屏蔽缓冲区的每个字节:

typedef uint8_t v32ui __attribute__ ((vector_size(32)));

void f(const uint8_t *begin, const uint8_t *end, uint8_t *o)
{
    for (; begin < end; begin += 32, o+=32)
      *(v32ui*) o = (*(v32ui*) begin) & 0x0fu;
}
typedef uint8_t v32ui_uuu属性(向量大小(32));
空f(常数uint8_*begin、常数uint8_*end、常数uint8_*o)
{
for(;begin
假设输入和输出缓冲区正确对齐(32字节),GCC verctor扩展是否支持并定义了这种转换

这是对字符串使用向量扩展最有效的方法吗

或者我必须显式地将字符串的部分存储/检索到向量中吗

例如:

void f(const uint8_t *begin, const uint8_t *end, uint8_t *o)
{
    for (; begin < end; begin += 32, o+=32) {
      v32ui t;
      memcpy(&t, begin, 32);
      t &= 0f0u;
      memcpy(o, &t, 32);
    }
}
void f(const uint8\u t*begin、const uint8\u t*end、uint8\u t*o)
{
for(;begin
还是有比
memcpy
更好/更有效的方法


当假设输入或输出缓冲区(或两者)未对齐时,如何安全/有效地使用向量扩展进行字符串处理?

向量需要在寄存器中处理,因此
memcpy
在这里不可能有用

如果自动矢量化不能生成好的代码,标准的技术是使用矢量内部函数。如果您可以使用能够在多个体系结构上编译为SIMD指令的ops实现所需的功能,那么gcc向量语法可能是一种很好的方法

我试用了GCC4.9.2的第一个版本。它生成的正是您所希望的,64位AVX。(256位加载、矢量和存储)

不使用
-march
或任何东西,只使用基线amd64(SSE2),它将输入复制到堆栈上的缓冲区,并从那里加载。我认为这是在输入/输出缓冲区未对齐的情况下进行的,而不仅仅是使用
movdqu
。不管怎么说,这是一个非常糟糕的慢代码,在GP寄存器中一次处理8个字节要比这个废话快得多

gcc-march=native-O3-sv32ui_和.c
(在Sandybridge上(没有AVX2的AVX)):

请注意,缺少标量清理或未对齐数据的处理<当地址对齐时,code>vmovdqu
vmovdqa
一样快,所以不使用它有点傻

gcc-O3-sv32ui_和.c
很奇怪

        .globl  f
f:
.LFB0:
        cmpq    %rsi, %rdi
        movdqa  .LC0(%rip), %xmm0  # load a vector of 0x0f bytes
        jnb     .L9
        leaq    8(%rsp), %r10
        andq    $-32, %rsp
        pushq   -8(%r10)
        pushq   %rbp
        movq    %rsp, %rbp
        pushq   %r10
        .p2align 4,,10
        .p2align 3
.L5:
        movq    (%rdi), %rax
        addq    $32, %rdi
        addq    $32, %rdx
        movq    %rax, -80(%rbp)
        movq    -24(%rdi), %rax
        movq    %rax, -72(%rbp)
        movq    -16(%rdi), %rax
        movdqa  -80(%rbp), %xmm1
        movq    %rax, -64(%rbp)
        movq    -8(%rdi), %rax
        pand    %xmm0, %xmm1
        movq    %rax, -56(%rbp)
        movdqa  -64(%rbp), %xmm2
        pand    %xmm0, %xmm2
        movaps  %xmm1, -112(%rbp)
        movq    -112(%rbp), %rcx
        movaps  %xmm2, -96(%rbp)
        movq    -96(%rbp), %rax
        movq    %rcx, -32(%rdx)
        movq    -104(%rbp), %rcx
        movq    %rax, -16(%rdx)
        movq    -88(%rbp), %rax
        movq    %rcx, -24(%rdx)
        movq    %rax, -8(%rdx)
        cmpq    %rdi, %rsi
        ja      .L5
        popq    %r10
        popq    %rbp
        leaq    -8(%r10), %rsp
.L9:
        rep ret
所以我想,如果gcc向量扩展有时会生成如此糟糕的代码,那么就不能安全地使用它。对于intrinsic,最简单的实现是:

#include <immintrin.h>
#include <stdint.h>
void f(const uint8_t *begin, const uint8_t *end, uint8_t *o)
{
    __m256i mask = _mm256_set1_epi8(0x0f);
    for (; begin < end; begin += 32, o+=32) {
        __m256i s = _mm256_loadu_si256((__m256i*)begin);
        __m256i d = _mm256_and_si256(s, mask);
        _mm256_storeu_si256( (__m256i*)o, d);
    }
}
#包括
#包括
空f(常数uint8_*begin、常数uint8_*end、常数uint8_*o)
{
__m256i掩码=_mm256_set1_epi8(0x0f);
for(;begin
这将生成与gcc矢量版本(使用AVX2编译)相同的代码。注意:这使用的是
VPAND
,而不是
VANDPS
,因此需要AVX2

对于大型缓冲区,在输入或输出缓冲区与16或32字节对齐、向量循环以及所需的任何标量清理之前,进行标量启动是值得的。对于较小的缓冲区,最好只使用未对齐的加载/存储和末尾的简单标量清理


由于您特别询问了字符串,如果字符串以nul结尾(隐式长度),则在跨越页面边界时必须小心,如果字符串在页面结尾之前结束,但您的读取跨越了边界,则不会出错。

对齐的大小写应该可以,但是你有没有检查gcc的矢量器在没有显式使用矢量的情况下是否还没有生成合适的代码?@marglisse,上面的屏蔽只是一个玩具示例。实际上,我想对字符串缓冲区中每个向量大小的增量应用一系列操作(字节移位、字节洗牌等)。类似问题:
#include <immintrin.h>
#include <stdint.h>
void f(const uint8_t *begin, const uint8_t *end, uint8_t *o)
{
    __m256i mask = _mm256_set1_epi8(0x0f);
    for (; begin < end; begin += 32, o+=32) {
        __m256i s = _mm256_loadu_si256((__m256i*)begin);
        __m256i d = _mm256_and_si256(s, mask);
        _mm256_storeu_si256( (__m256i*)o, d);
    }
}