代码之家  ›  专栏  ›  技术社区  ›  Verdagon

我可以用simd分类吗?

  •  2
  • Verdagon  · 技术社区  · 6 年前

    我对simd很好奇,想知道它是否能处理这个用例。

    假设我有一个2048个整数的数组,比如 [0x018A、0x004B、0x01C0、0x0234、0x098、0x0343、0x0222、0x0301、0x0398、0x087、0x0167、0x0389、0x03F2、0x0034、0x0345…]

    请注意它们都是如何以0x00、0x01、0x02或0x03开头的。我想把它们分成4个数组:

    • 一个用于所有以0x00开头的整数
    • 一个用于所有以0x01开头的整数
    • 一个用于所有以0x02开头的整数
    • 一个用于所有以0x03开头的整数

    我想我会有这样的代码:

    int main() {
       uint16_t in[2048] = ...;
    
       // 4 arrays, one for each category
       uint16_t out[4][2048];
    
       // Pointers to the next available slot in each of the arrays
       uint16_t *nextOut[4] = { out[0], out[1], out[2], out[3] };
    
       for (uint16_t *nextIn = in; nextIn < 2048; nextIn += 4) {
    
           (*** magic simd instructions here ***)
    
           // Equivalent non-simd code:
           uint16_t categories[4];
           for (int i = 0; i < 4; i++) {
               categories[i] = nextIn[i] & 0xFF00;
           }
           for (int i = 0; i < 4; i++) {
               uint16_t category = categories[i];
               *nextOut[category] = nextIn[i];
               nextOut[category]++;
           }
       }
       // Now I have my categoried arrays!
    }
    

    我想我的第一个内环不需要SIMD,它可能只是一个 (x & 0xFF00FF00FF00FF00) 指令,但我想知道我们是否可以把第二个内部循环变成一个SIMD指令。

    对于我正在做的这个“分类”操作,是否有任何类型的SIMD指令?

    “插入”说明似乎有点好用,但我太过环保,无法理解 https://software.intel.com/en-us/node/695331 .

    如果没有,有什么接近吗?

    谢谢!

    1 回复  |  直到 6 年前
        1
  •  3
  •   Peter Cordes    6 年前

    您可以使用simd来完成它,但是它的速度将取决于您可用的指令集以及您在实现中的聪明程度。

    一种方法是采用数组并“筛选”它来分离属于不同桶的元素。例如,从数组中获取32个字节,该数组将包含16个16位元素。使用一些 cmpgt 获取用于确定每个元素是否属于 00 + 01 桶或 02 + 03 桶。然后使用某种“压缩”或“过滤”操作将所有被屏蔽的元素连续地移动到寄存器的一端,然后对未被屏蔽的元素执行相同的操作。

    然后再重复一次以进行排序 00 01 02 03 .

    从AVX2开始 this question 关于“压缩”操作的灵感。使用AVX512,您可以使用 vcompress 帮助说明:它确实执行了这个操作,但只在32位或64位粒度下执行,因此您至少需要对每个向量执行一些操作。

    您还可以尝试垂直方法,在其中加载n个向量,然后在它们之间进行交换,以便第0个向量具有最小的元素等。此时,您可以使用更优化的算法来压缩阶段(例如,)。如果垂直排序足够多的向量,则末端的向量可能完全从 0x00 等)。

    最后,您还可以考虑以不同的方式组织您的数据,无论是在源代码处还是作为预处理步骤:分离出“类别”字节,该字节总是0-3的有效负载字节。许多处理步骤只需要在其中一个或另一个步骤上进行,因此您可以通过这种方式将它们分开来潜在地提高效率。例如,可以对所有类别的32个字节执行比较操作,然后对32个有效负载字节执行压缩操作(至少在最后一步中,每个类别都是唯一的)。

    这将导致字节元素数组,而不是16位元素,其中“类别”字节是隐式的。您已经将数据大小减少了一半,这可能会加快将来处理数据所需的其他操作。

    如果不能以这种格式生成源数据,则可以使用bucketing作为将有效负载放入正确bucket时移除标记字节的机会,因此输出为 uint8_t out[4][2048]; . 如果你在做模拟人生左包 pshufb 字节随机移动,如注释中所讨论的,您可以选择一个随机移动控制向量,它只将有效负载字节打包到下半部分中。

    (在AVX512BW之前,x86 SIMD没有任何变量控制字随机移动,只有字节或双字,因此您已经需要一个字节随机移动,它可以在将有效负载字节打包到底部的同时轻松地将有效负载与标记分开。)

    推荐文章