如何使用SIMD计算数组中字节的出现次数?

时间:2018-03-29 09:36:38

标签: c# .net simd system.numerics

给定以下输入字节:

var cart = [{
    "success": false,
    "message": "All Items Fetched",
    "cartItems": [{
            "_id": "5abca75f43b4c21ec482e96d",
            "title": "Apples",
            "price": 594,
            "quantity": 6,
            "prodId": "5aadb71792f47742d4e3749b",
            "__v": 0
        },
        {
            "_id": "5abca75f43b4c21ec482e96e",
            "title": "Red and Mixed Grapes",
            "price": 645,
            "quantity": 5,
            "prodId": "5aac5dac664a9042a44bf787",
            "__v": 0
        },
        {
            "_id": "5abca76143b4c21ec482e96f",
            "title": "AnanaB",
            "price": 445,
            "quantity": 5,
            "prodId": "5ab1c1a8044a584bdcad6e1d",
            "__v": 0
        }
    ]
}];

cart.forEach((data) => {
    for (var obj in data.cartItems) {
        console.log(data.cartItems[obj].quantity + ' x of ' + data.cartItems[obj].title);
    }
});

给定的面具:

var vBytes = new Vector<byte>(new byte[] {72, 101, 55, 08, 108, 111, 55, 87, 111, 114, 108, 55, 100, 55, 55, 20});

如何在输入数组中找到字节var mask = new Vector<byte>(55); 的计数?

我已尝试 xoring 55vBytes

mask

给出:

  

&lt; 127,82,0,91,91,88,0,96,88,69,91,0,03,0,0,35&gt;

但不知道我怎么能从中获得数。

为简单起见,我们假设输入字节长度始终等于var xored = Vector.Xor(mask, vBytes); 的大小。

3 个答案:

答案 0 :(得分:2)

感谢 Marc Gravell 的提示,以下作品:

print()

Marc有一个blog post,其中包含有关此主题的更多信息。

答案 1 :(得分:2)

在asm中,您希望pcmpeqb生成0或0xFF的向量。作为有符号整数处理,即0 / -1。

然后将compare-result用作整数值psubb,将0/1添加到该元素的计数器。 (减去-1 =加+1)

这可能会在256次迭代后溢出,因此在此之前的某个时间,对psadbw使用_mm_setzero_si128()将这些无符号字节(不会过低)水平地加总为64位整数(每组一个64位整数) 8个字节)。然后paddq累积64位总数。

在溢出之前累积可以使用嵌套循环完成,或者仅在常规展开循环结束时完成。 psadbw速度很快(因为它是视频编码运动搜索的关键构建块),因此只需每4个比较累积一次,甚至每1个并跳过{{1 }}

有关x86的更多详细信息,请参阅Agner Fog's optimization guides。根据他的指令表,psubb / psadbw xmm在Skylake上以每个时钟周期1个向量运行,具有3个周期延迟。 (只有1 uop的前端带宽。)上面提到的所有指令也是单uop,并且在多个端口上运行(因此不必为了吞吐量而相互冲突)。它们的128位版本只需要SSE2。

如果你真的一次只有一个向量来计数,而不是在内存上循环,那么可能vpsadbw ymm / pcmpeqb / psadbw(复制高一半到low)/ pshufd / paddd在整数寄存器中给出255 *匹配数。一个额外的向量指令(如从零减去,或与1减1或movd eax, xmm0(绝对值)将删除x255比例因子。

IDK如何在C#SIMD中编写,但你肯定想要一个点积!解包并转换为FP将比上面慢4倍,这是因为固定宽度向量比浮点数多4倍字节,pabsbdpps不< / em>快。 Skylake上的每1.5个循环吞吐量为4 uops,一个。如果必须对除无符号字节以外的其他内容进行水平求和,请参阅Fastest way to do horizontal float vector sum on x86(我的答案也包括整数)。

或者如果_mm_dp_ps对整数向量使用Vector.Dot / pmaddubsw,那么这可能不会那么糟糕,但为每个比较结果向量执行多步水平求和只是与pmaddwd相比较差,或者特别是偶尔为水平求和的字节累加器。

或者,如果C#使用psadbw的常量向量优化任何实际乘法。无论如何,这个答案的第一部分是您希望CPU运行的代码。实现这一点,但是你喜欢使用任何源代码来实现它。

答案 2 :(得分:1)

这里是C:

中的快速SSE2实现
size_t memcount_sse2(const void *s, int c, size_t n) {
   __m128i cv = _mm_set1_epi8(c), sum = _mm_setzero_si128(), acr0,acr1,acr2,acr3;
    const char *p,*pe;                                                                         
    for(p = s; p != (char *)s+(n- (n % (252*16)));) { 
      for(acr0 = acr1 = acr2 = acr3 = _mm_setzero_si128(),pe = p+252*16; p != pe; p += 64) { 
        acr0 = _mm_add_epi8(acr0, _mm_cmpeq_epi8(cv, _mm_loadu_si128((const __m128i *)p))); 
        acr1 = _mm_add_epi8(acr1, _mm_cmpeq_epi8(cv, _mm_loadu_si128((const __m128i *)(p+16)))); 
        acr2 = _mm_add_epi8(acr2, _mm_cmpeq_epi8(cv, _mm_loadu_si128((const __m128i *)(p+32)))); 
        acr3 = _mm_add_epi8(acr3, _mm_cmpeq_epi8(cv, _mm_loadu_si128((const __m128i *)(p+48))));
        __builtin_prefetch(p+1024);
      }
      sum = _mm_add_epi64(sum, _mm_sad_epu8(_mm_sub_epi8(_mm_setzero_si128(), acr0), _mm_setzero_si128()));
      sum = _mm_add_epi64(sum, _mm_sad_epu8(_mm_sub_epi8(_mm_setzero_si128(), acr1), _mm_setzero_si128()));
      sum = _mm_add_epi64(sum, _mm_sad_epu8(_mm_sub_epi8(_mm_setzero_si128(), acr2), _mm_setzero_si128()));
      sum = _mm_add_epi64(sum, _mm_sad_epu8(_mm_sub_epi8(_mm_setzero_si128(), acr3), _mm_setzero_si128()));
    }

    // may require SSE4, rewrite this part for actual SSE2.
    size_t count = _mm_extract_epi64(sum, 0) + _mm_extract_epi64(sum, 1);

    // scalar cleanup.  Could be optimized.
    while(p != (char *)s + n) count += *p++ == c;
    return count;
}

并查看:https://gist.github.com/powturbo for和avx2 implementation。