folly::F14NodeMap — stable pointer가 필요할 때
#한 줄 요약
F14NodeMap은 chunk slot에 value pointer만 두고 실제 value는 별도 heap node에 둔다. std::unordered_map처럼 reference/pointer가 rehash 후에도 유효하다. ValueMap보다 한 단계 indirection이 추가되지만 안정성을 얻는다.
#동기
F14ValueMap은 빠르지만 reference가 흔들린다. 다음 상황에서는 ValueMap을 쓸 수 없다.
- 값이 큰 객체(MB급 buffer)라 복사 비용이 부담.
- 다른 자료구조가 map value의 pointer를 보관 (graph node 등).
- value가 비복사 타입 (mutex, atomic).
- iterator가 오래 살아야 한다 (async chain 중간).
이런 use case는 std::unordered_map이 자연스럽지만 lookup 성능이 떨어진다. F14NodeMap은 F14의 SIMD probing + std-like pointer 안정성을 결합한다.
folly::F14NodeMap<int, std::mutex> mutex_pool;auto& m = mutex_pool[key]; // mutex는 movable 아님m.lock();// 다른 thread가 insert 해도 &m 유효#API & 사용법
ValueMap과 동일한 인터페이스.
#include <folly/container/F14Map.h>
folly::F14NodeMap<std::string, BigObject> m;m.emplace("key1", BigObject{...});
// reference 안정 — rehash 통과const auto& obj = m["key1"];m.reserve(10000); // rehash 가능LOG(INFO) << obj.id; // 여전히 OK
// iterator 안정 — std::unordered_map과 같음auto it = m.find("key1");m.emplace("key2", ...);it->second.UpdateField(); // 여전히 OK차이는 implementation detail이다. 사용자 코드는 Value ↔ Node만 바꾸면 된다(보통).
#내부 구현
#Slot 구조
ValueMap slot: [ Key | Value ] in-placeNodeMap slot: [ Node* ] heap node 가리킴 ↓ [ Key | Value ] (별도 heap)chunk 구조(14 slot + control byte)는 동일. 차이는 slot이 pointer라는 점.
// 약식 — folly/container/detail/F14Map-pre.htemplate <typename Key, typename Value>struct NodeContainerPolicy { using Item = std::pair<Key, Value>; using Slot = Item*; // pointer만 chunk에
static Item& itemAt(Slot& s) { return *s; } static Slot constructSlot(Item&& v, Allocator& a) { return new (a.allocate(1)) Item(std::move(v)); }};Slot이 Item*이므로 rehash로 chunk가 옮겨져도 node pointer 자체는 그대로. node 메모리는 처음 emplace 시 할당하고 erase 전까지 안 옮긴다.
#Insert 동작
// 약식auto emplace(K key, V value) { // 1. node를 먼저 heap에 할당 auto* node = allocator_.allocate(1); new (node) Item(std::move(key), std::move(value));
// 2. chunk에 slot pointer 저장 (rehash 가능) auto [slot, inserted] = tableEmplace(node->first, node); if (!inserted) { node->~Item(); allocator_.deallocate(node, 1); }}allocation 한 번 extra. 큰 value면 chunk 안에 inline 저장보다 오히려 메모리가 작다(chunk가 빈 slot까지 reserve하므로).
#Rehash 비용
ValueMap rehash는 value를 새 chunk로 move. value의 move cost가 영향.
NodeMap rehash는 pointer만 복사. value는 옮기지 않으므로 rehash 비용이 작다(특히 큰 value에서).
N=10K, sizeof(V)=1KB F14ValueMap rehash: ~10ms (move 10K개의 1KB) F14NodeMap rehash: ~0.1ms (pointer 10K개만)ValueMap이 빠르려면 value가 작아야 한다. value가 커지면 NodeMap의 indirection 비용 < ValueMap의 move 비용.
#std/abseil 비교
// abseilabsl::node_hash_map<K, V> m; // std-like pointer 안정
// stdstd::unordered_map<K, V> std_map; // pointer 안정 (chaining)
// follyfolly::F14NodeMap<K, V> f14n;| 항목 | std::unordered_map | absl::node_hash_map | folly::F14NodeMap |
|---|---|---|---|
| pointer 안정 | O | O | O |
| iterator 안정 | O (insert/erase 다른 노드는) | O | O |
| 구조 | chaining | Swiss + pointer slot | Swiss + pointer slot |
| Lookup | cache miss 많음 | SIMD 가속 | SIMD 가속 |
| Insert allocation | node 매번 | node 매번 | node 매번 |
absl::node_hash_map과 사상 동일. 성능은 거의 같다. ValueMap 대비 lookup이 한 indirection만큼 (보통 10-20%) 느리다.
#코드 리뷰 포인트
// Bad — small value인데 NodeMapfolly::F14NodeMap<int, int> tiny;// 매 entry마다 16-byte 정도 heap alloc
// Goodfolly::F14ValueMap<int, int> tiny; // chunk inlinevalue가 작으면 NodeMap의 heap allocation 오버헤드가 일반 lookup 비용을 압도한다. ValueMap 또는 FastMap.
// Good — 큰 value, 외부 pointer 필요folly::F14NodeMap<UserId, UserProfile> profiles;const UserProfile* p = &profiles.at(id);// 다른 thread/chain이 p를 가져다 쓰는 동안 insert 안전// Bad — value가 movable이라고 ValueMap을 쓰지만 lifetime 위반folly::F14ValueMap<int, std::string> m;auto& s = m[1];m.insert({2, "..."}); // rehash 가능s += "x"; // UB
// Good — NodeMapfolly::F14NodeMap<int, std::string> mn;auto& s = mn[1];mn.insert({2, "..."});s += "x"; // OK#안티패턴
mutex/atomic/condition_variable같은 비이동 타입을 ValueMap에: rehash 시 move가 컴파일 에러 또는 UB. 항상 NodeMap.- node hash 함수와 key compare가 같지 않은 두 map을 섞기: extract/merge 시 ABI 호환이 깨진다. 두 map 모두 동일 trait.
- node를 직접
delete시도:extract()API로 안전한 ownership transfer. 임의 delete는 chunk 상태 무효화.
#정리
F14NodeMap은 chunk에 pointer만, value는 heap node.std::unordered_map수준의 pointer/reference 안정성.- 큰 value, 비이동 타입, 외부 pointer 노출 시 선택.
- 작은 value에는 overhead 크다 — ValueMap 우선.
absl::node_hash_map과 등가.
#다음 편
F14VectorMap은 또 다른 접근이다. value를 별도 std::vector에 두고 chunk에는 index만 둔다. cache-friendly iteration이 가능하다.
#관련 항목
- Part 7-01: F14ValueMap — inline value 변형
- Part 7-03: F14VectorMap — vector backing
- Part 7-04: F14FastMap — 자동 선택
- 원문 — folly/container/F14Map.h
Folly Code Review · 32 of 89
- 1 Folly Code Review — Meta의 production-grade C++ 라이브러리 코드 분석
- 2 Folly 개요 — Meta가 production에서 검증한 utility 모음 분석
- 3 Folly vs Abseil 철학 비교 — performance-first vs std-compatible
- 4 Folly 빌드와 fbcode 환경 — monorepo의 그림자
- 5 Folly API stability 정책 — 어떤 보장도 없다는 솔직함
- 6 Folly production validation 문화 — peta-scale에서 단련된 코드
- 7 folly::Future 분석 — std::future의 한계를 넘는 composable async
- 8 folly::Promise·makeFuture — Future를 만드는 두 길
- 9 folly::SemiFuture vs Future — executor binding의 명시화
- 10 folly::Future thenValue·thenError·thenTry — continuation 체인 분석
- 11 folly::collect·collectAll·collectAny — fan-in 패턴 분석
- 12 folly::Future retry·window·via — 제어 흐름 조합자
- 13 folly::fibers 분석 — M:N stackful coroutine
- 14 folly::InlineExecutor — 호출자 thread에서 즉시 실행
- 15 folly::CPUThreadPoolExecutor — CPU-bound 작업의 표준 thread pool
- 16 folly::IOThreadPoolExecutor — libevent 기반 I/O pool
- 17 folly::ManualExecutor — 결정적 테스트를 위한 수동 진행
- 18 folly::EventBase 분석 — libevent 이벤트 루프의 핵심
- 19 folly::IOBuf 분석 — zero-copy buffer chain의 기본 단위
- 20 folly::IOBufQueue — chain의 push/pull 추상화
- 21 folly::io::Cursor·RWCursor — chain 위의 stream
- 22 folly Zero-copy 패턴 — IOBuf로 ScatterGather I/O 표현
- 23 folly::IOBuf shared semantics — clone·unshare·takeOwnership
- 24 folly::FBString 분석 — SSO + COW 구현
- 25 folly의 fmt::format 통합 — 모던 포맷팅 채택
- 26 folly::StringPiece — string_view 호환 분석
- 27 folly Join·Split utilities — 문자열 분해와 결합
- 28 folly::to·tryTo — text↔num 변환 분석
- 29 folly Conv Customization — 사용자 타입 지원
- 30 folly Conv 성능 비교 — sprintf·stringstream 대비
- 31 folly::F14ValueMap vs std::unordered_map
- 32 folly::F14NodeMap — stable pointer가 필요할 때
- 33 folly::F14VectorMap — cache-friendly iteration
- 34 folly::F14FastMap — auto-select 동작
- 35 folly F14 internals — SIMD probing 메커니즘
- 36 folly::small_vector — inline storage 분석
- 37 folly::FixedString — compile-time string
- 38 folly::AtomicHashMap — lock-free read 분석
- 39 folly::ConcurrentHashMap — sharded 동시 해시 맵
- 40 folly::EvictingCacheMap — LRU 구현 분석
- 41 folly::Synchronized — lock wrapper 패턴
- 42 folly::SharedMutex 분석
- 43 folly::Baton — one-shot wait 동기화
- 44 folly::RWSpinLock 분석
- 45 folly::PicoSpinLock — 1-byte spinlock
- 46 folly::ProducerConsumerQueue — SPSC 큐 분석
- 47 folly::MPMCQueue — multi-producer multi-consumer
- 48 folly::UnboundedQueue — 동적 크기 lock-free
- 49 folly::fibers::Channel — Go-like channel
- 50 folly::dynamic — JSON-like dynamic type 분석
- 51 folly JSON conversion — toJson·parseJson
- 52 folly dynamic ↔ struct — manual marshaling
- 53 folly dynamic Visitor pattern — type별 분기
- 54 folly::Singleton vs Meyers/static — 왜 Folly의 Singleton인가
- 55 folly::SingletonVault 분석 — 등록·소멸·의존성
- 56 folly::Singleton try_get·try_get_fast — TLS-cached 접근
- 57 folly::ExceptionWrapper — type-erased exception holder
- 58 folly::ScopeGuard·SCOPE_EXIT — RAII cleanup
- 59 folly::Optional vs std::optional
- 60 folly::Function vs std::function
- 61 folly::Lazy — 지연 초기화 wrapper
- 62 folly Meta 스타일 code review 패턴
- 63 folly anti-patterns — 잘못 쓰면 std보다 느림
- 64 folly vs std 선택 기준 분석
- 65 folly::coro 개요 — production C++20 코루틴 어댑터
- 66 folly::coro::Task — lazy single-shot 코루틴
- 67 folly::coro::AsyncGenerator — 비동기 스트림
- 68 folly coro blockingWait·collectAll — 동기 경계와 fan-in
- 69 folly::coro::Baton·Mutex — 코루틴-aware 동기화
- 70 folly::Expected — 결과 또는 오류
- 71 folly::Try — Future 결과 wrapper
- 72 folly::Try vs Expected 선택 기준
- 73 folly::Range — 일반 iterator pair
- 74 folly::Uri — URL 파서
- 75 folly Fingerprint64·128 — 분산 hash
- 76 folly SpookyHashV2 — fast non-crypto hash
- 77 folly::Init — main() 부트스트랩
- 78 folly::Indestructible — global lifetime 패턴
- 79 folly::MicroLock — 1-byte 락
- 80 folly::MicroSpinLock — 가장 좁은 spin lock
- 81 folly::format — legacy formatter 분석
- 82 folly::demangle — typeid 디망글링
- 83 folly::DynamicConverter — dynamic ↔ struct
- 84 folly::RecordIO — append-only 로그 파일 포맷
- 85 folly::io::Compression — zstd·lz4·snappy wrapper
- 86 folly::AsyncIO — io_uring·Linux AIO
- 87 folly::CancellationToken — 코루틴·Future 취소 전파
- 88 folly::observer — hot config의 atomic refresh
- 89 fbcode 패턴 모음 — folly 사용의 실전
관련 글
folly F14 internals — SIMD probing 메커니즘
F14 chunk 구조와 SIMD probing — SSE2/AVX/NEON dispatch, H1/H2 hash split, 14-slot 선택 이유.
같은 시리즈에서 이어 읽기
folly::F14FastMap — auto-select 동작
F14FastMap — key/value 크기로 ValueMap과 VectorMap 중 자동 선택, 사용자 trade-off 제거.
같은 시리즈에서 이어 읽기
folly::F14VectorMap — cache-friendly iteration
F14VectorMap — value를 contiguous vector에 두고 chunk에는 index만, 순회 cache-friendly.
같은 시리즈에서 이어 읽기