algorithm - 优化数组压缩

标签 algorithm matlab sse simd

假设我有一个数组 k = [1 2 0 0 5 4 0]

我可以按如下方式计算掩码 m = k > 0 = [1 1 0 0 1 1 0]

只使用mask m和下面的操作

  1. 左移/右移
  2. 和/或
  3. 加/减/乘

我可以将 k 压缩成以下内容 [1 2 5 4]

这是我目前的做法(MATLAB 伪代码):

function out = compact( in )
    d = in
    for i = 1:size(in, 2) %do (# of items in in) passes
        m = d > 0
        %shift left, pad w/ 0 on right
        ml = [m(2:end) 0] % shift
        dl = [d(2:end) 0] % shift

        %if the data originally has a gap, fill it in w/ the 
        %left shifted one
        use = (m == 0) & (ml == 1) %2 comparison  

        d = use .* dl + ~use .* d

        %zero out elements that have been moved to the left
        use_r = [0 use(1:end-1)]
        d = d .* ~use_r
    end

    out = d(1 : size(find(in > 0), 2)) %truncate the end
end

直觉

每次迭代,我们将掩码左移并比较掩码。如果我们发现在这个移位之后,一个原本为 void(mask[i] = 0) 的索引现在有效(mask[i] = 1),那么我们设置一个索引让数据左移。

问题

上述算法复杂度为 O(N * (3 shift + 2 comparison + AND + add + 3 multiplys))。有没有办法提高它的效率?

最佳答案

原始伪代码中没有太多需要优化的地方。我在这里看到了几个小改进:

  • loop 可以少执行一次迭代(即 size-1),
  • 如果 'use' 为零,您可以提前中断循环,
  • use = (m == 0) & (ml == 1) 可能会简化为 use = ~m & ml,
  • 如果~算作单独的操作,最好使用倒置形式:use = m | ~ml, d = ~use .* dl + use .* d, use_r = [1 use(1:end-1)], d = d .*use_r

但是可以发明更好的算法。算法的选择取决于使用的 CPU 资源:

  • Load-Store Unit,即将算法直接应用于内存字。在芯片制造商将高度并行的 SCATTER 指令添加到他们的指令集中之前,这里什么也做不了。
  • SSE 寄存器,即在整个 16 个字节的寄存器上工作的算法。像提议的伪代码这样的算法在这里无济于事,因为我们已经有了各种可以使工作更好的随机/排列指令。将各种比较指令与 PMOVMSKB 结合使用,将结果按 4 位分组并在 switch/case 下应用各种混洗指令(如 LastCoder 所述)是我们能做的最好的事情。
  • 带有最新指令集的 SSE/AVX 寄存器提供了更好的方法。我们可以直接使用 PMOVMSKB 的结果,将其转换为 PSHUFB 之类的控制寄存器。
  • 整数寄存器,即 GPR 寄存器或同时处理 SSE/AVX 寄存器的多个 DWORD/QWORD 部分(允许执行多个独立压缩)。提议的应用于整数寄存器的伪代码允许压缩任何长度(从 2 到 20 位)的二进制子集。这是我的算法,它的性能可能会更好。

C++,64 位,子集宽度 = 8:

typedef unsigned long long ull;
const ull h = 0x8080808080808080;
const ull l = 0x0101010101010101;
const ull end = 0xffffffffffffffff;

// uncompacted bytes
ull x = 0x0100802300887700;

// set hi bit for zero bytes (see D.Knuth, volume 4)
ull m = h & ~(x | ((x|h) - l));

// bitmask for nonzero bytes
m = ~(m | (m - (m>>7)));

// tail zero bytes need no special treatment
m |= (m - 1);

while (m != end)
{
  ull tailm = m ^ (m + 1); // bytes to be processed
  ull tailx = x & tailm; // get the bytes
  tailm |= (tailm << 8); // shift 1 byte at a time
  m |= tailm; // all processed bytes are masked
  x = (x ^ tailx) | (tailx << 8); // actual byte shift
}

关于algorithm - 优化数组压缩,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7886628/

相关文章:

algorithm - 删除一些边后的DFS

apache-flex - Flex 4 或 Actionscript 可以访问共享的 C 或 C++ 库吗?

matlab - 使用 MATLAB 求大向量的交集

c - x86_64 SSE 对齐 : differences between GCC and Clang

simd - FFTW 是否动态确定 SIMD 版本?

c# - 数组中数字的排列

string - 近似字符串匹配的具体算法代码

c++ - 通过索引获取 __m128 的成员?

c++ - 计算交换次数以插入以递增顺序对整数数组进行排序的有效方法

python - 我如何能够加快 Matlab 中的滑动窗口以进行操作对象识别?