web-dev-qa-db-ja.com

Intel固有の命令を利用してCRC32を実装する方法は?

Intelには特定の CRC32命令 SSE4.2命令セットで使用できます。この命令を利用してCRC32計算を高速化するにはどうすればよいですか?

12
ecerulm

まず、IntelのCRC32命令はCRC-32Cを計算するのに役立ちます(つまり、通常のCRC32とは異なる多項式を使用します。 WikipediaCRC32 エントリを見てください)

gccを使用してCRC32Cにインテルのハードウェアアクセラレーションを使用するには、次のことができます。

  1. asmステートメントによるCコードのインラインアセンブリ言語
  2. 組み込み関数_mm_crc32_u8_mm_crc32_u16_mm_crc32_u32または_mm_crc32_u64を使用します。インテルのコンパイラーiccの説明については、 インテル組み込み関数ガイド を参照してください。ただし、gccもそれらを実装しています。

これは、一度に1バイトかかる__mm_crc32_u8でそれを行う方法です。__mm_crc32_u64を使用すると、一度に8バイトかかるため、パフォーマンスがさらに向上します。

uint32_t sse42_crc32(const uint8_t *bytes, size_t len)
{
  uint32_t hash = 0;
  size_t i = 0;
  for (i=0;i<len;i++) {
    hash = _mm_crc32_u8(hash, bytes[i]);
  }

  return hash;
}

これをコンパイルするには、CFLAGS-msse4.2を渡す必要があります。 gcc -g -msse4.2 test.cと同様に、それ以外の場合はundefined reference to _mm_crc32_u8について文句を言います。

実行可能ファイルが実行されているプラ​​ットフォームで命令が使用できない場合にプレーンC実装に戻したい場合は、GCCのifunc属性を使用できます。お気に入り

uint32_t sse42_crc32(const uint8_t *bytes, size_t len)
{
  /* use _mm_crc32_u* here */
}

uint32_t default_crc32(const uint8_t *bytes, size_t len)
{
  /* pure C implementation */
}

/* this will be called at load time to decide which function really use */
/* sse42_crc32 if SSE 4.2 is supported */
/* default_crc32 if not */
static void * resolve_crc32(void) {
  __builtin_cpu_init();
  if (__builtin_cpu_supports("sse4.2")) return sse42_crc32;

  return default_crc32;
}

/* crc32() implementation will be resolved at load time to either */
/* sse42_crc32() or default_crc32() */
uint32_t crc32(const uint8_t *bytes, size_t len) __attribute__ ((ifunc ("resolve_crc32")));
11
ecerulm

CRC-32Cの高速ハードウェアおよびソフトウェア実装については、 この回答 を参照してください。ハードウェア実装は、速度のために3つのcrc32命令を並行して効果的に実行します。

3
Mark Adler