【问题标题】:What's the most efficient way to make bitwise operations in a C array在 C 数组中进行按位运算的最有效方法是什么
【发布时间】:2010-10-14 16:10:36
【问题描述】:

我有一个像这样的 C 数组:

char byte_array[10];

还有一个充当面具的:

char byte_mask[10];

我想在每个字节上使用按位运算得到另一个数组,该数组是第一个数组加上第二个数组的结果。

最有效的方法是什么?

感谢您的回答。

【问题讨论】:

    标签: c performance micro-optimization


    【解决方案1】:
    \#define CHAR_ARRAY_SIZE    (10)
    \#define INT_ARRAY_SIZE     ((CHAR_ARRAY_SIZE/ (sizeof (unsigned int)) + 1)
    
    typedef union _arr_tag_ {
    
        char          byte_array [CHAR_ARRAY_SIZE];
        unsigned int  int_array [INT_ARRAY_SIZE]; 
    
    } arr_tag;
    

    现在 int_array 用于屏蔽。这可能适用于 32 位和 64 位处理器。

    arr_tag arr_src, arr_result, arr_mask;
    
    for (int i = 0; i < INT_ARRAY_SIZE; i ++) {
        arr_result.int_array [i] = arr_src.int_array[i] & arr_mask.int_array [i];
    }
    

    试试这个,代码也可能看起来很干净。

    【讨论】:

      【解决方案2】:

      如果您想让它更快,请确保 byte_array 的长度是 4 的倍数(在 64 位机器上为 8),然后:

      char byte_array[12];
      char byte_mask[12];
      /* Checks for proper alignment */
      assert(((unsigned int)(void *)byte_array) & 3 == 0);
      assert(((unsigned int)(void *)byte_mask) & 3 == 0);
      for (i = 0; i < (10+3)/4; i++) {
        ((unsigned int *)(byte_array))[i] &= ((unsigned int *)(byte_mask))[i];
      }
      

      这比逐字节执行要快得多。

      (请注意,这是就地突变;如果您还想保留原始的 byte_array,那么您显然需要将结果存储在另一个数组中。)

      【讨论】:

      • 10/4 == 2,所以这只处理 8 个字符。此外,在某些非 x86 架构上,由于内存访问未对齐,这可能会引发总线错误。
      • bk1e:你是对的,我
      • 如果不是4/8的倍数,使用duff的设备:)
      【解决方案3】:
      for ( i = 10 ; i-- > 0 ; )
          result_array[i] = byte_array[i] & byte_mask[i];
      
      • 向后预加载处理器缓存行。
      • 在比较中包含减量可以节省一些指令。

      这适用于所有阵列和处理器。但是,如果您知道您的数组是字对齐的,那么更快的方法是转换为更大的类型并执行相同的计算。

      例如,假设n=16 而不是n=10。那么这会更快:

      uint32_t* input32 = (uint32_t*)byte_array;
      uint32_t* mask32 = (uint32_t*)byte_mask;
      uint32_t* result32 = (uint32_t*)result_array;
      for ( i = 4 ; i-- > 0 ; )
          result32[i] = input32[i] & mask32[i];
      

      (当然,uint32_t 需要正确的类型,如果 n 不是 2 的幂,则需要清理开头和/或结尾,以便对齐 32 位内容。)

      变化:这个问题特别要求将结果放在一个单独的数组中,但是几乎可以肯定的是,就地修改输入数组会更快。

      【讨论】:

      • 等等,缓存预取器反向工作更好吗?我以为它只会向前预取。
      • 担心预加载处理器缓存行似乎是一种严重的过早优化。
      • @Trent -- 问题的point 是优化。倒退也不会慢,所以你也可以。 @Crashworks - 请记住,缓存行是对齐的,通常在大量边界上,因此通常它必须在您要求的字节之前提取字节。
      • 任何关于缓存的声明都将是特定于处理器的。我没有看到 OP 在哪里说明此代码将在什么硬件上执行。
      • 我很欣赏这个解释。我会用这个方法。我不完全了解缓存,所以我无法真正了解该级别发生了什么。
      猜你喜欢
      • 2021-06-28
      • 2012-02-02
      • 2011-05-06
      • 1970-01-01
      • 2011-07-13
      • 2020-04-11
      • 2013-01-23
      • 1970-01-01
      • 2017-06-13
      相关资源
      最近更新 更多