jjzjj

c++ - 将打包的半字节组合成打包的字节

coder 2024-02-22 原文

给定一个或多个 __m128i__m256i每个 16 位元素包含一个半字节,将它们组合并打包为每个 8 位元素一个字节的最快方法是什么(即 (hi << 4) | lo 用于相邻的 16 位元素)?

这是我想出的最好的方法,不幸的是,它与标量代码相当:

const static __m256i shufmask = _mm256_setr_epi8(
  2, 255, 255, 255, 6, 255, 255, 255, 10, 255, 255, 255, 14, 255, 255, 255,
  2, 255, 255, 255, 6, 255, 255, 255, 10, 255, 255, 255, 14, 255, 255, 255);

const static __m256i high4 = _mm256_setr_epi8(
  255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0,
  255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0
);

inline static int64_t hnib2byte(__m256i nibbles) { // (a << 4) | b;
  // hi 0 lo 0, ...
  __m256i upper = _mm256_slli_epi16(nibbles, 4);

  // Align upper and lower halves so they can be ORed vertically
  // lo 0 0 0, ...
  __m256i lower = _mm256_shuffle_epi8(nibbles, shufmask);

  // ab x x x, ...
  __m256i or = _mm256_or_si256(upper, lower);

  // Pack into bytes
  or = _mm256_and_si256(or, high4);
  __m256i pack16 = _mm256_packus_epi16(or, or);
  const int _3to2 = 0b00001000;
  __m256i perm16 = _mm256_permute4x64_epi64(pack16, _3to2); // :(
  __m256i pack8 = _mm256_packus_epi16(perm16, perm16);

  return _mm_cvtsi128_si64(_mm256_castsi256_si128(pack8));
}

包括 AVX2 在内的指令都是公平的。 AVX-512 中的掩码移位提供了更好的选择。这是在循环中调用的,因此尽早将半字节打包成 8 位元素之类的事情也是公平的游戏。

最佳答案

下面的解决方案 hnib2byte_v2 应该比您的解决方案快一点,至少在 Intel 处理器上是这样。

指令 vpermd 或内在 _mm256_permutevar8x32_epi32 在 AMD Ryzen 上运行缓慢。在该平台上,最好使用 _mm256_extracti128_si256 提取 pck 的高 128 位 channel ,使用 _mm256_castsi256_si128 提取低 128- bit lane,并将这两个与 _mm256_or_si256 结合起来,得到最低 64 位的答案。

/*
gcc -O3 -m64 -Wall -mavx2 -march=broadwell nibble2byte.c
*/
#include <immintrin.h>
#include <stdio.h>
#include <stdint.h>

int print_avx2_hex(__m256i ymm);


inline static int64_t hnib2byte_v2(__m256i nibbles) {
  __m256i shufmask8  = _mm256_set_epi8(-1,-1,-1,-1,  -1,-1,-1,-1,  14,10,6,2,  -1,-1,-1,-1,  -1,-1,-1,-1,  -1,-1,-1,-1,  -1,-1,-1,-1,  14,10,6,2);
  __m256i shufmask32 = _mm256_set_epi32(7,7,7,7,7,7,5,0);

  __m256i lower      = _mm256_slli_epi32(nibbles, 20);
// 00E0000000C00000 00A0000000800000 0060000000400000 0020000000000000

  __m256i up_lo      = _mm256_or_si256(lower,nibbles);
// 00EF000E00CD000C 00AB000A00890008 0067000600450004 0023000200010000

  __m256i pck        = _mm256_shuffle_epi8(up_lo,shufmask8);
// 0000000000000000 EFCDAB8900000000 0000000000000000 0000000067452301

  __m256i pck64      = _mm256_permutevar8x32_epi32(pck,shufmask32);
// 0000000000000000 0000000000000000 0000000000000000 EFCDAB8967452301

//  print_avx2_hex(lower);
//  print_avx2_hex(up_lo);
//  print_avx2_hex(pck);
//  print_avx2_hex(pck64);

  return _mm_cvtsi128_si64(_mm256_castsi256_si128(pck64));
}


inline static int64_t hnib2byte(__m256i nibbles) { // (a << 4) | b;

__m256i shufmask = _mm256_setr_epi8(
  2, 255, 255, 255, 6, 255, 255, 255, 10, 255, 255, 255, 14, 255, 255, 255,
  2, 255, 255, 255, 6, 255, 255, 255, 10, 255, 255, 255, 14, 255, 255, 255);

__m256i high4 = _mm256_setr_epi8(
  255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0,
  255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0);

  // hi 0 lo 0, ...
  __m256i upper = _mm256_slli_epi16(nibbles, 4);

  // Align upper and lower halves so they can be ORed vertically
  // lo 0 0 0, ...
  __m256i lower = _mm256_shuffle_epi8(nibbles, shufmask);

  // ab x x x, ...
  __m256i or = _mm256_or_si256(upper, lower);

  // Pack into bytes
  or = _mm256_and_si256(or, high4);
  __m256i pack16 = _mm256_packus_epi16(or, or);
  const int _3to2 = 0b00001000;
  __m256i perm16 = _mm256_permute4x64_epi64(pack16, _3to2); // :(
  __m256i pack8 = _mm256_packus_epi16(perm16, perm16);

  return _mm_cvtsi128_si64(_mm256_castsi256_si128(pack8));
}


int print_avx2_hex(__m256i ymm)
{
    long unsigned int x[4];
        _mm256_storeu_si256((__m256i*)x,ymm);
        printf("%016lX %016lX %016lX %016lX\n", x[3],x[2],x[1],x[0]);

    return 0;
}


int main()
{
   uint64_t x;
    __m256i nibble_x16 = _mm256_set_epi16(0x000F,0x000E,0x000D,0x000C,  0x000B,0x000A,0x0009,0x0008,  
                                          0x0007,0x0006,0x0005,0x0004,  0x0003,0x0002,0x0001,0x0000);
    printf("AVX variable: \n");
    print_avx2_hex(nibble_x16);                                      
    x = hnib2byte(nibble_x16);
    printf("With hnib2byte    x = %016lX \n\n",x);

    printf("AVX variable: \n");
    print_avx2_hex(nibble_x16);                                      
    x = hnib2byte_v2(nibble_x16);
    printf("With hnib2byte_v2 x = %016lX \n",x);
    return 0;
}

输出是:

$ ./a.out
AVX variable: 
000F000E000D000C 000B000A00090008 0007000600050004 0003000200010000
With hnib2byte    x = EFCDAB8967452301 

AVX variable: 
000F000E000D000C 000B000A00090008 0007000600050004 0003000200010000
With hnib2byte_v2 x = EFCDAB8967452301 

对于此处选择的输入,这两种方法的输出是相等的。

除了应该在循环外完成的加载洗牌常量之外,它只编译为五个指令: vpslld,vpor,vpshufb,vpermd,和vmovq,少了三个比你的解决方案。

关于c++ - 将打包的半字节组合成打包的字节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46134763/

有关c++ - 将打包的半字节组合成打包的字节的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  3. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  4. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  5. ruby - 最多 n 的组合 - 2

    给定一个数组a,什么是实现其组合直到第n的最佳方法?例如:a=%i[abc]n=2#Expected=>[[],[:a],[:b],[:c],[:a,b],[:b,:c],[:c,:a]] 最佳答案 做如下:a=%w[abc]n=30.upto(n).flat_map{|i|a.combination(i).to_a}#=>[[],["a"],["b"],["c"],["a","b"],#["a","c"],["b","c"],["a","b","c"]] 关于ruby-最多n的组合,我

  6. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

  7. += 的 Ruby 方法 - 2

    有没有办法让Ruby能够做这样的事情?classPlane@moved=0@x=0defx+=(v)#thisiserror@x+=v@moved+=1enddefto_s"moved#{@moved}times,currentxis#{@x}"endendplane=Plane.newplane.x+=5plane.x+=10putsplane.to_s#moved2times,currentxis15 最佳答案 您不能在Ruby中覆盖复合赋值运算符。任务在内部处理。您应该覆盖+,而不是+=。plane.a+=b与plane.a=

  8. ruby - Rails 组合多个 activerecord 关系 - 2

    我想合并多个事件记录关系例如,apple_companies=Company.where("namelike?","%apple%")banana_companies=Company.where("namelike?","%banana%")我想结合这两个关系。不是合并,合并是apple_companies.merge(banana_companies)=>Company.where("namelike?andnamelike?","%apple%","%banana%")我要Company.where("名字像?还是名字像?","%apple%","%banana%")之后,我会写代

  9. ruby - Sinatra + Heroku + Datamapper 使用 dm-sqlite-adapter 部署问题 - 2

    出于某种原因,heroku尝试要求dm-sqlite-adapter,即使它应该在这里使用Postgres。请注意,这发生在我打开任何URL时-而不是在gitpush本身期间。我构建了一个默认的Facebook应用程序。gem文件:source:gemcuttergem"foreman"gem"sinatra"gem"mogli"gem"json"gem"httparty"gem"thin"gem"data_mapper"gem"heroku"group:productiondogem"pg"gem"dm-postgres-adapter"endgroup:development,:t

  10. ruby - Ruby 中字符串运算符 + 和 << 的区别 - 2

    我是Ruby和这个网站的新手。下面两个函数是不同的,一个在函数外修改变量,一个不修改。defm1(x)x我想确保我理解正确-当调用m1时,对str的引用被复制并传递给将其视为x的函数。运算符当调用m2时,对str的引用被复制并传递给将其视为x的函数。运算符+创建一个新字符串,赋值x=x+"4"只是将x重定向到新字符串,而原始str变量保持不变。对吧?谢谢 最佳答案 String#+::str+other_str→new_strConcatenation—ReturnsanewStringcontainingother_strconc

随机推荐