absl::ComputeCrc32c — 하드웨어 가속 체크섬
#한 줄 요약
absl::ComputeCrc32c는 Castagnoli 다항식 (0x1EDC6F41) 기반의 CRC32C를 계산한다. x86 SSE4.2의 CRC32 명령어, ARMv8.1의 CRC32CB/CW/CX 명령어를 자동 활용해 GB/s 단위 처리량을 낸다. iSCSI·Btrfs·gRPC·protobuf payload 체크섬에서 표준이다.
#동기
분산 시스템은 거의 모든 transport에 체크섬이 필요하다. MD5/SHA는 암호학적 강도를 위해 비용을 지불하지만 일반 무결성 검사에는 과하다. 반면 CRC32 (IEEE 802.3, ethernet에 쓰이는 다항식 0x04C11DB7)는 충돌률이 높다.
CRC32C(Castagnoli)는 두 측면 모두에서 좋은 균형이다.
- 같은 32bit 출력, 더 나은 충돌률 (특히 short message).
- SSE4.2 + ARMv8 CRC 명령어가 hardware-accelerated. byte당 1 cycle 미만.
Google이 GFS·Spanner·BigTable·Colossus 어디서나 쓰는 표준이고 protobuf의 Cord 체크섬도 이 알고리즘이다.
런타임에 hardware/software 경로가 갈리는 흐름은 다음과 같다.
#API와 사용법
#include "absl/crc/crc32c.h"
absl::string_view data = "hello";absl::crc32c_t c = absl::ComputeCrc32c(data);
// 누적 계산 (chunk 단위)absl::crc32c_t c = absl::crc32c_t{0};c = absl::ExtendCrc32cByZeroes(c, /*length=*/16); // zero-fill 처리c = absl::ExtendCrc32c(c, chunk1);c = absl::ExtendCrc32c(c, chunk2);
// concat 결합 (스트림 합치기)absl::crc32c_t c = absl::ConcatCrc32c(c1, c2, /*length_b=*/c2_length);
// 비교는 ==/!=, 값 추출은 static_castuint32_t raw = static_cast<uint32_t>(c);타입이 uint32_t가 아닌 strong typedef crc32c_t라는 점이 중요하다. 일반 정수와 혼동해 XOR/+로 합치는 실수를 컴파일러가 잡는다.
// 회피 — type system이 차단uint32_t a = absl::ComputeCrc32c(d); // 컴파일 에러
// Goodabsl::crc32c_t a = absl::ComputeCrc32c(d);#내부 구현 — 하드웨어 분기
absl/crc/internal/crc32c.cc는 빌드 시점에 가용한 명령어 셋에 따라 path를 고른다.
// 요약crc32c_t ComputeCrc32c(absl::string_view data) {#if ABSL_HAVE_X86_INTRINSICS && CPU_HAS_SSE42 return ComputeCrc32cX86(data);#elif ABSL_HAVE_ARM_CRC32 return ComputeCrc32cArm(data);#else return ComputeCrc32cSoftware(data);#endif}런타임 CPUID 체크도 있다. 빌드 시 SSE4.2가 켜져 있지 않더라도 런타임에 가용하면 _mm_crc32_u64 경로를 쓴다.
#x86 path
_mm_crc32_u64는 8byte씩 한 사이클에 누적한다. cache line 단위로 3개의 stream을 병렬 처리해 superscalar pipeline을 최대로 채우는 기법이 일반적.
// 간소화uint32_t crc = 0xFFFFFFFF;const uint8_t* p = data;const uint8_t* end = data + len;
while (p + 8 <= end) { crc = _mm_crc32_u64(crc, *reinterpret_cast<const uint64_t*>(p)); p += 8;}while (p < end) { crc = _mm_crc32_u8(crc, *p); ++p;}return crc ^ 0xFFFFFFFF;실제 Abseil은 3-way slicing과 PCLMULQDQ folding까지 동원해 4KB 이상 chunk에서 16~20 GB/s를 낸다.
#ARM path
ARMv8.1의 CRC32C{B,H,W,X} 명령어로 1/4/8 byte 단위 누적. apple silicon (M1+)에서 동일 명령어를 지원해 macOS 빌드에서도 가속.
#Software fallback
slice-by-8 table lookup. 250 MB/s 정도. 임베디드 ARM7 등에 사용된다.
#성능 (참고치)
x86 1MB 입력 기준:
| 알고리즘 | 처리량 |
|---|---|
| MD5 | 700 MB/s |
| SHA-256 (with SHA-NI) | 1.5 GB/s |
| CRC32C software (slice-by-8) | 1 GB/s |
| CRC32C SSE4.2 | 16 GB/s |
| CRC32C PCLMULQDQ 3-way | 25 GB/s |
암호학적 hash와 비교해 수십 배 빠르다. RPC payload 한 줄에 정도의 비용.
#활용 — Cord와의 통합
absl::Cord는 chunk별 CRC를 누적 보관하는 기능을 가진다. ConcatCrc32c로 chunk를 합칠 때 다시 전체를 스캔하지 않는다.
absl::Cord c1 = Read(file1);absl::Cord c2 = Read(file2);c1.Append(c2); // 내부적으로 두 CRC를 ConcatCrc32c로 결합
absl::crc32c_t total = c1.ExpectedChecksum().value_or(absl::crc32c_t{});수 GB의 데이터를 체크섬 재계산 없이 합칠 수 있다. 분산 storage에서 chunk re-assembly에 매우 유용.
#코드 리뷰 포인트
1. uint32_t로 받는 코드는 즉시 교체
// 회피uint32_t crc = absl::ComputeCrc32c(data).operator uint32_t();
// Goodabsl::crc32c_t crc = absl::ComputeCrc32c(data);type erasure 비용이 없는 strong typedef를 그대로 활용.
2. 누적 계산은 ExtendCrc32c
// 회피 — chunk마다 새 CRC 후 XORauto a = absl::ComputeCrc32c(chunk1);auto b = absl::ComputeCrc32c(chunk2);auto combined = static_cast<uint32_t>(a) ^ static_cast<uint32_t>(b); // 의미 없음
// Goodauto c = absl::ComputeCrc32c(chunk1);c = absl::ExtendCrc32c(c, chunk2);CRC는 단순 XOR로 합쳐지지 않는다. ExtendCrc32c 또는 ConcatCrc32c를 쓴다.
3. 표준 다항식 일치 확인
다음 모두 CRC32C(Castagnoli)와 동일한 결과를 낸다. 데이터 교환 호환성을 위한 체크 포인트.
crc32c명령어 (Linux util)- Google
crc32c라이브러리 - Btrfs/iSCSI 표준
- protobuf
Crc32cCombine
반면 ethernet CRC32(다항식 0x04C11DB7)는 다른 알고리즘이다. CRC32만 적혀 있으면 어느 쪽인지 확인 필수.
4. boundary alignment
// 큰 입력은 자동으로 8 byte 정렬 처리absl::ComputeCrc32c(buffer); // 내부에서 align loop호출 측에서 정렬할 필요는 없다.
#자주 보는 안티패턴
암호학적 무결성에 CRC32C
CRC32C는 임의 데이터 무결성용이지 공격자의 위조 방어용이 아니다. 공격자가 의도적으로 충돌을 만드는 시나리오에서는 SHA-256 / BLAKE3 / HMAC이 옳다.
CRC32(ethernet)와 혼동
// boost::crc는 기본이 ethernet CRC32boost::crc_32_type c;c.process_bytes(data, len);uint32_t v = c.checksum(); // CRC32C와 다른 값protobuf·gRPC·iSCSI 모두 CRC32C. ethernet CRC32와 섞이지 않도록.
short message에 SHA
// 회피 — 8byte hash에 SHA256 (32byte 출력 + cost)auto h = SHA256(small_key);
// Good — 8byte 식별자에 CRC32C 또는 FarmHashabsl::crc32c_t h = absl::ComputeCrc32c(small_key);key fingerprint·shard 분배에는 CRC32C나 absl::Hash (Part 10-01).
incremental verification 누락
// 회피 — 메모리에 전부 적재 후 한 번에 계산std::string entire = ReadAll(file);auto crc = absl::ComputeCrc32c(entire);
// Good — chunk 단위 누적absl::crc32c_t crc = absl::crc32c_t{0};char buf[64 * 1024];while (size_t n = Read(file, buf, sizeof(buf))) { crc = absl::ExtendCrc32c(crc, absl::string_view(buf, n));}대용량 file·stream은 누적 계산이 필수. CPU + memory 양쪽이 절약된다.
network endian 가정
// 회피 — 직접 byte로 쓰면 endian 의존uint32_t v = static_cast<uint32_t>(crc);write(fd, &v, 4); // little endian 환경에서 0x12345678 → 78 56 34 12
// Good — 표준 byte order로uint32_t v = htonl(static_cast<uint32_t>(crc));write(fd, &v, 4);체크섬을 transport에 실을 때 endian을 명시. iSCSI/protobuf 모두 network byte order 표준.
#정리
absl::ComputeCrc32c는 Castagnoli CRC32C, hardware-accelerated.crc32c_tstrong typedef로 일반 정수와 분리.ExtendCrc32c/ConcatCrc32c로 chunk 합산.- 16~25 GB/s, SHA256보다 10배 이상 빠름.
Cord통합으로 분산 storage에서 chunk re-assembly가 자연스러움.- 암호학적 보안에는 부적합.
#다음 편
Part 16-03 — PeriodicSampler에서 sampling rate 조정 도구를 본다.
#관련 항목
- Part 16-03 — PeriodicSampler
- Part 16-01 — Stacktrace / Symbolize
- Part 15-01 — Cord — Cord 체크섬 통합
- Part 10-01 — AbslHashValue
- Folly Part 9-02 — Hash
Abseil Code Review · 78 of 79
- 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
- 2 Abseil 개요 — Google이 std를 보완한 이유
- 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
- 4 Abseil 빌드와 의존성 — Bazel vs CMake
- 5 Abseil LTS vs HEAD 릴리스 모델 분석
- 6 Abseil Versioning과 ABI 호환성 정책
- 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
- 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
- 9 absl::LogSeverity — 로그 레벨 타입
- 10 Abseil type_traits — negation·conjunction·void_t
- 11 Abseil Conformance·Policy 분석
- 12 Abseil Memory utilities 분석
- 13 Abseil raw_logging — heap-free 로깅
- 14 Abseil thread_annotations — clang TSA 통합
- 15 absl::Status — exception-free error handling
- 16 absl::StatusOr<T> — 값 또는 에러
- 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
- 18 absl::Status payload — 구조화된 에러 컨텍스트
- 19 absl::Status ↔ exception 변환 패턴
- 20 absl::string_view — non-owning 문자열 참조
- 21 absl::string_view 함정 — dangling·c_str·임시 객체
- 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
- 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
- 24 absl::StrJoin — 컨테이너 결합과 Formatter
- 25 absl::StrFormat — type-safe printf·FormatSpec
- 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
- 27 Abseil Escape — CEscape·HexEscape·Base64
- 28 absl::flat_hash_map — Swiss Table 기반 hash map
- 29 absl::flat_hash_set — set 버전 Swiss Table
- 30 absl::node_hash_map — stable pointer가 필요할 때
- 31 absl::btree_map — sorted·cache-friendly B-tree
- 32 absl::FixedArray — 런타임 크기 stack 배열
- 33 absl::InlinedVector — small buffer optimization
- 34 Abseil Swiss Table internals — control byte·SIMD probing
- 35 absl::Mutex — reader-writer·fairness·deadlock 검출
- 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
- 37 absl::Notification — once-only signal
- 38 absl::BlockingCounter·Barrier — 다중 thread 조율
- 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
- 40 absl::Time·Duration 분석 — 단단한 type
- 41 absl::Time Format·Parse
- 42 absl::CivilTime 분석
- 43 absl::time_zone 분석
- 44 absl::Time mocking — 테스트 친화 시간
- 45 absl::BitGen — 모던 난수 생성기
- 46 Abseil Random Distributions — Uniform·Exponential
- 47 Abseil Mocking Random — 테스트 결정성
- 48 Abseil Random Seeding·Entropy
- 49 absl::int128·uint128 분석
- 50 absl::bits — popcount·countl_zero
- 51 absl::optional vs std::optional
- 52 absl::variant 분석
- 53 absl::span 분석
- 54 absl::any 분석
- 55 absl::compare — three-way 비교
- 56 Abseil utility — apply·in_place
- 57 Abseil AbslHashValue 분석
- 58 Abseil HashState chaining
- 59 Abseil Custom hashable 구현
- 60 Abseil LOG·VLOG·CHECK 분석
- 61 Abseil LogSink 분석
- 62 Abseil LogEntry·structured logging
- 63 Abseil Stack trace·failure_signal_handler
- 64 ABSL_FLAG 정의 분석
- 65 Abseil ParseCommandLine 동작
- 66 Abseil Flag introspection·validation
- 67 Google 스타일의 Abseil 사용 패턴
- 68 Abseil 자주 보는 anti-pattern
- 69 std → absl 마이그레이션 전략
- 70 absl::Cleanup — 함수 종료 시 실행 보장
- 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
- 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
- 73 absl::bind_front와 Overload — 함수 객체 보조 도구
- 74 absl::Cord — 분산 시스템용 대용량 문자열
- 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
- 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
- 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
- 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
- 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피
관련 글
absl::PeriodicSampler — 적응형 샘플링·jitter 회피
absl::profiling_internal::PeriodicSampler — sampling rate를 동적으로 조정, geometric distribution으로 jitter 회피. 메모리 할당 추적·profiling 인프라의 기반.
같은 시리즈에서 이어 읽기
absl::GetStackTrace와 Symbolize — crash 시 readable stack
absl::GetStackTrace로 PC 배열을 받고 absl::Symbolize로 함수 이름·파일·라인으로 변환. signal handler 안에서도 동작하는 async-safe API.
같은 시리즈에서 이어 읽기
absl::Cord vs std::string — 선택 기준과 메모리 프로파일
absl::Cord와 std::string 중 무엇을 쓸지 판단하는 기준 — 크기·mutation 패턴·공유 빈도·메모리 프로파일 비교.
같은 시리즈에서 이어 읽기