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

复制未对齐的位数组的高效算法是什么?

  •  6
  • Jamie  · 技术社区  · 14 年前

    在过去我已经做过很多次了,我从来没有对结果感到满意。

    有没有人能建议一种快速的方法,将一个连续的位数组从源复制到目标,在这种情况下,源和目标可能不会在方便的处理器边界上对齐(右移)?

    如果源和目标都没有对齐,那么问题很快就会变成只有其中一个没有对齐(在第一个副本之后)。

    const char mask[8] = { 1, 3, 7, 15, 31, 63, 127, 255 };
    /* Assume:
     * - destination is already zeroed,
     * - offsets are right shifts
     * - bits to copy is big (> 32 say)
     */
    int bitarray_copy(char * src, int src_bit_offset, int src_bit_len,
                      char * dst, int dst_bit_offset) {
        if (src_bit_offset == dst_bit_offset) { /* Not very interesting */ 
        } else {
            int bit_diff_offset = src_bit_offset - dst_bit_offset; /* assume positive */
            int loop_count;
            char c;
            char mask_val = mask[bit_diff_offset];
    
            /* Get started, line up the destination. */
            c  = (*src++ << bit_diff_offset) | ((*src >> (8 - bit_diff_offset)) & mask_val);
            c &= mask[8-dst_bit_offset];
    
            *dst++ |= c;
    
            src_bit_len -= 8 - dst_bit_offset;
            loop_count = src_bit_len >> 3;
    
            while (--loop_count >= 0) 
                * dst ++ = (*src++ << bit_diff_offset) | ((*src >> (8 - bit_diff_offset)) & mask_val);
    
            /* Trailing tail copy etc ... */
            if (src_bit_len % 8) /* ... */
        }
    }
    

    4 回复  |  直到 14 年前
        1
  •  1
  •   Mark Ransom    14 年前

    您的内部循环接受两个字节的片段并将它们移动到目标字节。这几乎是最佳的。以下是一些没有特别顺序的提示:

    • 没有必要一次只限制一个字节。使用最大的整数大小你的平台将让你逃脱。这当然会使你的开始和结束逻辑复杂化。
    • 如果确实需要掩码,请确保编译器将表查找移到循环之外。如果不是,则将其复制到一个临时变量并使用它。
        2
  •  7
  •   Jamie    10 年前

    这就是我最后要做的。(

    #include <limits.h>
    #include <string.h>
    #include <stddef.h>
    
    #define PREPARE_FIRST_COPY()                                      \
        do {                                                          \
        if (src_len >= (CHAR_BIT - dst_offset_modulo)) {              \
            *dst     &= reverse_mask[dst_offset_modulo];              \
            src_len -= CHAR_BIT - dst_offset_modulo;                  \
        } else {                                                      \
            *dst     &= reverse_mask[dst_offset_modulo]               \
                  | reverse_mask_xor[dst_offset_modulo + src_len];    \
             c       &= reverse_mask[dst_offset_modulo + src_len];    \
            src_len = 0;                                              \
        } } while (0)
    
    
    static void
    bitarray_copy(const unsigned char *src_org, int src_offset, int src_len,
                        unsigned char *dst_org, int dst_offset)
    {
        static const unsigned char mask[] =
            { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff };
        static const unsigned char reverse_mask[] =
            { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
        static const unsigned char reverse_mask_xor[] =
            { 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x00 };
    
        if (src_len) {
            const unsigned char *src;
                  unsigned char *dst;
            int                  src_offset_modulo,
                                 dst_offset_modulo;
    
            src = src_org + (src_offset / CHAR_BIT);
            dst = dst_org + (dst_offset / CHAR_BIT);
    
            src_offset_modulo = src_offset % CHAR_BIT;
            dst_offset_modulo = dst_offset % CHAR_BIT;
    
            if (src_offset_modulo == dst_offset_modulo) {
                int              byte_len;
                int              src_len_modulo;
                if (src_offset_modulo) {
                    unsigned char   c;
    
                    c = reverse_mask_xor[dst_offset_modulo]     & *src++;
    
                    PREPARE_FIRST_COPY();
                    *dst++ |= c;
                }
    
                byte_len = src_len / CHAR_BIT;
                src_len_modulo = src_len % CHAR_BIT;
    
                if (byte_len) {
                    memcpy(dst, src, byte_len);
                    src += byte_len;
                    dst += byte_len;
                }
                if (src_len_modulo) {
                    *dst     &= reverse_mask_xor[src_len_modulo];
                    *dst |= reverse_mask[src_len_modulo]     & *src;
                }
            } else {
                int             bit_diff_ls,
                                bit_diff_rs;
                int             byte_len;
                int             src_len_modulo;
                unsigned char   c;
                /*
                 * Begin: Line things up on destination. 
                 */
                if (src_offset_modulo > dst_offset_modulo) {
                    bit_diff_ls = src_offset_modulo - dst_offset_modulo;
                    bit_diff_rs = CHAR_BIT - bit_diff_ls;
    
                    c = *src++ << bit_diff_ls;
                    c |= *src >> bit_diff_rs;
                    c     &= reverse_mask_xor[dst_offset_modulo];
                } else {
                    bit_diff_rs = dst_offset_modulo - src_offset_modulo;
                    bit_diff_ls = CHAR_BIT - bit_diff_rs;
    
                    c = *src >> bit_diff_rs     &
                        reverse_mask_xor[dst_offset_modulo];
                }
                PREPARE_FIRST_COPY();
                *dst++ |= c;
    
                /*
                 * Middle: copy with only shifting the source. 
                 */
                byte_len = src_len / CHAR_BIT;
    
                while (--byte_len >= 0) {
                    c = *src++ << bit_diff_ls;
                    c |= *src >> bit_diff_rs;
                    *dst++ = c;
                }
    
                /*
                 * End: copy the remaing bits; 
                 */
                src_len_modulo = src_len % CHAR_BIT;
                if (src_len_modulo) {
                    c = *src++ << bit_diff_ls;
                    c |= *src >> bit_diff_rs;
                    c     &= reverse_mask[src_len_modulo];
    
                    *dst     &= reverse_mask_xor[src_len_modulo];
                    *dst |= c;
                }
            }
        }
    }
    
        3
  •  1
  •   supercat    14 年前

    什么是最佳的将取决于目标平台。在某些没有桶形移位器的平台上,将整个向量右移或左移一位n次(n<3)将是最快的方法(在PIC18平台上,一个8x展开字节循环将左移一位每8字节需要11个指令周期)。否则,我喜欢这个模式(注意src2必须初始化,这取决于您想对缓冲区的末尾执行什么操作)

      src1 = *src++;
      src2 = (src1 shl shiftamount1) | (src2 shr shiftamount2);
      *dest++ = src2;
      src2 = *src++;
      src1 = (src2 shl shiftamount1) | (src1 shr shiftamount2);
      *dest++ = src1;
    

      src0 = *src++;
      src1 = *src++;
      src2 = *src++;
      src3 = *src++;
      tmp  = src0;
      src0 = src0 shr shiftamount1
      src0 = src0 | src1 shl shiftamount2
      src1 = src1 shr shiftamount1
      src1 = src1 | src2 shl shiftamount2
      src2 = src2 shr shiftamount1
      src2 = src2 | src3 shl shiftamount2
      src3 = src3 shr shiftamount1
      src3 = src3 | tmp shl shiftamount2
      *dest++ = src0;
      *dest++ = src1;
      *dest++ = src2;
      *dest++ = src3;
    

    每旋转16字节11条指令。

        4
  •  0
  •   Karl Bielefeldt    14 年前

    推荐文章