Abseil HashState chaining
#HashState의 정체
AbslHashValue 함수가 받는 H는 HashState concept을 만족하는 타입이다. Abseil 내부에서는 absl::hash_internal::MixingHashState 같은 구체 구현이 들어오지만 사용자는 추상 인터페이스만 본다.
template <typename H>friend H AbslHashValue(H h, const MyType& v) { // h: HashState — 누적된 해시 상태 // 반환: 새 HashState return H::combine(std::move(h), v.field1, v.field2);}HashState는 immutable에 가까운 모델 — combine은 새 상태를 반환한다.
#combine — 기본 chaining
template <typename H>friend H AbslHashValue(H h, const Order& o) { return H::combine(std::move(h), o.id, o.user_id, o.amount, o.timestamp);}combine(h, x1, x2, ..., xN)은 순차적으로 각 인자를 누적한다. 내부적으로는 다음과 동등.
h = AbslHashValue(std::move(h), x1);h = AbslHashValue(std::move(h), x2);...return h;각 인자에 대해 그 타입의 AbslHashValue를 ADL로 찾아 호출.
#combine_contiguous — 연속 메모리 최적화
같은 타입의 N개를 한 번에 hash할 때.
template <typename H>friend H AbslHashValue(H h, const Histogram& hg) { return H::combine_contiguous(std::move(h), hg.bins.data(), hg.bins.size());}combine_contiguous는 trivially-hashable 타입(int, char, float 등)이면 바이트 단위 묶어 한 번에 처리한다. 일반 combine을 N번 부르는 것보다 빠르다.
#combine_unordered — set·map 친화
set·unordered_map의 hash는 순서에 무관 해야 한다.
template <typename H>friend H AbslHashValue(H h, const Bag& b) { return H::combine_unordered(std::move(h), b.items.begin(), b.items.end());}내부적으로 각 원소의 hash를 commutative하게 결합(XOR가 아닌 안전한 방식). 같은 원소집합이면 어떤 순서로 들어와도 같은 결과.
absl::flat_hash_set·absl::flat_hash_map의 hash가 이걸 쓴다.
#H::combine의 의미론
combine은 다음을 약속한다.
- 결정적 — 같은 시드, 같은 입력이면 같은 결과.
- 분포 좋음 — 약한 비트가 없도록 mixing 함수 적용.
- 순서 의존 —
combine(h, a, b)와combine(h, b, a)는 다른 결과(원하면combine_unordered사용). - 타입 의존 —
int 0과string ""이 같은 결과를 내지 않음.
마지막 항목이 중요하다. 사용자가 직접 h = h * 31 + x 같은 코드를 짜면 (0, "")와 ("", 0)이 같은 결과를 낼 수 있다. combine은 타입 인코딩을 포함해 이런 충돌을 막는다.
#가변 alternative — variant
variant의 hash는 index + active value 를 함께 섞는다.
template <typename H>friend H AbslHashValue(H h, const absl::variant<int, std::string>& v) { return H::combine(std::move(h), v.index(), absl::visit([](auto&& x) { return absl::Hash<std::decay_t<decltype(x)>>{}(x); }, v));}Abseil이 variant에 대해 기본 AbslHashValue를 제공하므로 사용자는 보통 직접 짤 일이 없다. 단, 자체 sum type에 hash가 필요하면 같은 패턴.
#작은 예시 — nested struct
struct Address { std::string street; std::string city;
template <typename H> friend H AbslHashValue(H h, const Address& a) { return H::combine(std::move(h), a.street, a.city); }};
struct Person { std::string name; Address address; std::vector<std::string> phones;
template <typename H> friend H AbslHashValue(H h, const Person& p) { // address: 그 자체로 AbslHashValue 정의 → combine이 위임 // phones: vector<string>은 std로 제공된 hash 사용 return H::combine(std::move(h), p.name, p.address, p.phones); }};combine이 각 인자의 AbslHashValue를 ADL로 찾아 재귀적으로 합성한다. 사용자는 평탄한 한 줄만 쓴다.
#회피 패턴
// 회피 — 직접 mixingtemplate <typename H>friend H AbslHashValue(H h, const Bad& b) { size_t s = std::hash<int>{}(b.x); s ^= std::hash<std::string>{}(b.y) << 1; // ❌ 약한 분포 return H::combine(std::move(h), s);}
// Goodtemplate <typename H>friend H AbslHashValue(H h, const Bad& b) { return H::combine(std::move(h), b.x, b.y);}// 회피 — set hash인데 순서 의존template <typename H>friend H AbslHashValue(H h, const MySet& s) { return H::combine_contiguous(std::move(h), s.items.data(), s.items.size()); // ❌ {1,2,3}과 {3,2,1}이 다른 해시}
// Good — unordered 헬퍼template <typename H>friend H AbslHashValue(H h, const MySet& s) { return H::combine_unordered(std::move(h), s.items.begin(), s.items.end());}// 회피 — std::move 누락template <typename H>friend H AbslHashValue(H h, const X& x) { return H::combine(h, x.a); // ❌ rvalue 권장}
// Goodreturn H::combine(std::move(h), x.a);H는 가벼운 값 타입 으로 설계되어 move가 cheap하지만, 관례적으로 std::move를 명시한다.
#표 — combine 변형
| 메서드 | 용도 |
|---|---|
combine(h, x1, x2, ...) | 순서 있는 필드 chaining |
combine_contiguous(h, ptr, n) | 같은 타입 연속 — 빠름 |
combine_unordered(h, it1, it2) | 순서 무관 (set·map) |
#정리
H::combine은 순차적으로 각 필드를 누적. 약한 mixing은 알아서 처리.- 같은 타입 연속 메모리 →
combine_contiguous(성능). - 순서 무관 집합 →
combine_unordered(의미). - HashState는 이동 친화 —
std::move(h)로 관례적 전달. combine은 타입 인코딩을 포함 → 다른 타입의 같은 비트값이 충돌하지 않음.
#다음 장 예고
Part 10-03: Custom hashable — 실전 사용자 타입 hash 패턴.
#관련 항목
- Part 10-01: AbslHashValue
- Part 5-07: Swiss table internals — Swiss table이 hash bits를 어떻게 쓰는지
- 원문 — Hash State
Abseil Code Review · 58 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 회피
관련 글
Abseil Custom hashable 구현
실전 사용자 타입을 hashable로 만들기 — value class, enum, pair, raw 바이트 등 흔한 패턴 정리.
같은 시리즈에서 이어 읽기
Abseil AbslHashValue 분석
AbslHashValue — std::hash 특수화 대신 ADL 기반 friend 함수로 hash를 정의하는 Abseil의 방식.
같은 시리즈에서 이어 읽기
absl::PeriodicSampler — 적응형 샘플링·jitter 회피
absl::profiling_internal::PeriodicSampler — sampling rate를 동적으로 조정, geometric distribution으로 jitter 회피. 메모리 할당 추적·profiling 인프라의 기반.
같은 시리즈에서 이어 읽기