본문으로 건너뛰기
Folly Code Review · 33/89

folly::F14FastMap — auto-select 동작

· Hawk · 4분 읽기

#한 줄 요약

F14FastMapsizeof(pair<K,V>)를 보고 컴파일 타임에 F14ValueMap(작은 value) 또는 F14VectorMap(큰 value)으로 alias 된다. 사용자가 trade-off를 고민하지 않아도 적절한 변형을 얻는다.

#동기

세 가지 변형(Value/Node/Vector)이 있으면 매번 무엇을 쓸지 결정해야 한다. 대부분의 코드는 굳이 차이를 고민할 필요가 없다 — 대충 빠르면 된다.

  • value가 작으면 chunk inline이 cache-friendly → ValueMap.
  • value가 크면 chunk slot에 큰 객체가 들어가면 메모리 낭비 → VectorMap.

이 결정을 컴파일러가 한다. sizeof(Item)이 임계치(48 byte) 이하면 ValueMap, 초과면 VectorMap.

folly::F14FastMap<int, int> // → ValueMap (sizeof(pair) = 8)
folly::F14FastMap<std::string, BigObj> // → VectorMap (sizeof(pair) > 48)

#API & 사용법

API는 ValueMap과 동일하다. 단, 어느 variant로 alias 되었느냐에 따라 세부 동작이 다르다.

#include <folly/container/F14Map.h>
folly::F14FastMap<int, std::string> small;
// VectorMap으로 dispatch (pair<int, string> = 40 bytes... 임계치 부근)
folly::F14FastMap<int, int> tiny;
// ValueMap (8 bytes)
folly::F14FastMap<std::string, std::array<char, 256>> big;
// VectorMap
// 모두 같은 인터페이스
small.emplace(1, "a");
small.find(1);
for (auto& [k, v] : small) { ... }

VectorMap이 선택된 경우 insertion order가 유지된다. ValueMap이면 순서 없다. 코드가 양쪽을 모두 동작해야 하면 순서를 가정하면 안 된다.

#내부 구현

#간단한 type dispatch

// 약식 — folly/container/F14Map.h
namespace detail {
constexpr size_t kF14VectorMapThreshold = 24;
template <typename K, typename V>
using FastSelect = std::conditional_t<
(sizeof(std::pair<K, V>) <= kF14VectorMapThreshold) &&
folly::IsRelocatable<std::pair<K, V>>::value,
F14ValueMap<K, V>,
F14VectorMap<K, V>>;
}
template <typename K, typename V>
using F14FastMap = detail::FastSelect<K, V>;

(정확한 임계치와 trait는 folly 버전마다 다르다. core idea는 단순 if constexpr.)

조건:

  1. sizeof(pair) ≤ 임계치 (작은 value).
  2. is_trivially_relocatable (rehash move 안전).

둘 다 만족하면 ValueMap, 아니면 VectorMap.

#Trivially relocatable

folly는 자체 trait IsRelocatable을 가진다. std::pair, primitive, POD는 자동으로 relocatable. 사용자 타입은 다음과 같이 표시.

struct Big {
std::array<char, 64> buf;
};
namespace folly { template <> struct IsRelocatable<Big> : std::true_type {}; }

relocatable이면 rehash 시 byte-wise memmove로 옮길 수 있어 ValueMap이 안전하다. 아니면 VectorMap이 선택돼 옮길 일이 적다.

#사용 결정 — 언제 FastMap

F14FastMap을 쓰는 경우:

  • 정책을 정하기 번거롭고 그냥 빠른 것이면 된다.
  • workload가 lookup-heavy도, iteration-heavy도 명확하지 않다.
  • team 코드 베이스에서 일관성을 원한다.

명시적 선택을 쓰는 경우:

  • lookup만 한다면 F14ValueMap.
  • 순회/snapshot이 잦다면 F14VectorMap.
  • pointer 안정성이 필요하면 F14NodeMap.
  • 큰 value이고 erase가 잦다면 F14NodeMap (VectorMap의 swap-pop 회피).

대부분 새 코드는 F14FastMap이 default.

#std/abseil 비교

abseil에는 자동 선택이 없다. flat_hash_map 또는 node_hash_map을 사용자가 명시.

항목abseilfolly
자동 선택XF14FastMap
명시 variantsflat, nodeValue, Node, Vector
Insertion orderXVectorMap만
Trait basedXis_trivially_relocatable

folly의 FastMap은 abseil 식 명시적 선택에 대한 한 가지 답. 코드 작성 부담을 낮춘다.

#코드 리뷰 포인트

// Good — 일반적 경우 FastMap
folly::F14FastMap<int, int> counters;
// Bad — pointer 안정성 필요한데 FastMap
folly::F14FastMap<int, std::string> m;
auto* p = &m[1];
m[2] = "..."; // rehash 가능, ValueMap으로 alias 되면 UB

FastMap이 어느 variant인지 모르므로 reference 안정성을 요구하는 코드는 명시적으로 NodeMap. FastMap은 “포인터 유지 안 한다”는 약속.

// 주의 — order에 의존하면 FastMap 안 됨
folly::F14FastMap<int, int> m;
m.insert({1, 1}); m.insert({2, 2});
for (auto& [k, v] : m) {
// 순서가 1, 2일 수도, 2, 1일 수도
}

FastMap의 순서는 컴파일 결과에 따라 다르다. 순서가 의미 있으면 F14VectorMap을 명시.

// Good — value 크기 명시적
struct Stat { uint64_t count; uint64_t total; }; // 16 byte
folly::F14FastMap<int, Stat> stats; // ValueMap으로 dispatch

작은 POD value면 FastMap이 ValueMap을 선택해 최적.

#안티패턴

  • F14FastMap을 NodeMap 대체로 사용: FastMap은 절대로 NodeMap을 선택하지 않는다. pointer 안정성 필요하면 NodeMap 명시.
  • value type을 incomplete type으로 FastMap에: sizeof(pair)를 계산해야 하므로 complete type 필요. forward declared type은 못 씀.
  • 순서가 깨지는 곳에서 VectorMap default 가정: FastMap이 int → int 같은 작은 type을 받으면 ValueMap 선택. 순서 없다.

#정리

  • F14FastMapsizeof(pair) 기반 컴파일 타임 dispatch.
  • 작은 value → F14ValueMap, 큰 value → F14VectorMap.
  • F14NodeMap은 자동 선택 안 함 — 필요하면 명시.
  • order/pointer 안정성에 의존하지 않는 일반 코드의 default.
  • variant가 무엇인지 모르므로 변형별 특수 동작 가정 금지.

#다음 편

마지막으로 F14의 SIMD probing 내부를 본다. control byte 구조, H1/H2 hash split, NEON/SSE2 dispatch.

#관련 항목

Folly Code Review · 34 of 89

  1. 1 Folly Code Review — Meta의 production-grade C++ 라이브러리 코드 분석
  2. 2 Folly 개요 — Meta가 production에서 검증한 utility 모음 분석
  3. 3 Folly vs Abseil 철학 비교 — performance-first vs std-compatible
  4. 4 Folly 빌드와 fbcode 환경 — monorepo의 그림자
  5. 5 Folly API stability 정책 — 어떤 보장도 없다는 솔직함
  6. 6 Folly production validation 문화 — peta-scale에서 단련된 코드
  7. 7 folly::Future 분석 — std::future의 한계를 넘는 composable async
  8. 8 folly::Promise·makeFuture — Future를 만드는 두 길
  9. 9 folly::SemiFuture vs Future — executor binding의 명시화
  10. 10 folly::Future thenValue·thenError·thenTry — continuation 체인 분석
  11. 11 folly::collect·collectAll·collectAny — fan-in 패턴 분석
  12. 12 folly::Future retry·window·via — 제어 흐름 조합자
  13. 13 folly::fibers 분석 — M:N stackful coroutine
  14. 14 folly::InlineExecutor — 호출자 thread에서 즉시 실행
  15. 15 folly::CPUThreadPoolExecutor — CPU-bound 작업의 표준 thread pool
  16. 16 folly::IOThreadPoolExecutor — libevent 기반 I/O pool
  17. 17 folly::ManualExecutor — 결정적 테스트를 위한 수동 진행
  18. 18 folly::EventBase 분석 — libevent 이벤트 루프의 핵심
  19. 19 folly::IOBuf 분석 — zero-copy buffer chain의 기본 단위
  20. 20 folly::IOBufQueue — chain의 push/pull 추상화
  21. 21 folly::io::Cursor·RWCursor — chain 위의 stream
  22. 22 folly Zero-copy 패턴 — IOBuf로 ScatterGather I/O 표현
  23. 23 folly::IOBuf shared semantics — clone·unshare·takeOwnership
  24. 24 folly::FBString 분석 — SSO + COW 구현
  25. 25 folly의 fmt::format 통합 — 모던 포맷팅 채택
  26. 26 folly::StringPiece — string_view 호환 분석
  27. 27 folly Join·Split utilities — 문자열 분해와 결합
  28. 28 folly::to·tryTo — text↔num 변환 분석
  29. 29 folly Conv Customization — 사용자 타입 지원
  30. 30 folly Conv 성능 비교 — sprintf·stringstream 대비
  31. 31 folly::F14ValueMap vs std::unordered_map
  32. 32 folly::F14NodeMap — stable pointer가 필요할 때
  33. 33 folly::F14VectorMap — cache-friendly iteration
  34. 34 folly::F14FastMap — auto-select 동작
  35. 35 folly F14 internals — SIMD probing 메커니즘
  36. 36 folly::small_vector — inline storage 분석
  37. 37 folly::FixedString — compile-time string
  38. 38 folly::AtomicHashMap — lock-free read 분석
  39. 39 folly::ConcurrentHashMap — sharded 동시 해시 맵
  40. 40 folly::EvictingCacheMap — LRU 구현 분석
  41. 41 folly::Synchronized — lock wrapper 패턴
  42. 42 folly::SharedMutex 분석
  43. 43 folly::Baton — one-shot wait 동기화
  44. 44 folly::RWSpinLock 분석
  45. 45 folly::PicoSpinLock — 1-byte spinlock
  46. 46 folly::ProducerConsumerQueue — SPSC 큐 분석
  47. 47 folly::MPMCQueue — multi-producer multi-consumer
  48. 48 folly::UnboundedQueue — 동적 크기 lock-free
  49. 49 folly::fibers::Channel — Go-like channel
  50. 50 folly::dynamic — JSON-like dynamic type 분석
  51. 51 folly JSON conversion — toJson·parseJson
  52. 52 folly dynamic ↔ struct — manual marshaling
  53. 53 folly dynamic Visitor pattern — type별 분기
  54. 54 folly::Singleton vs Meyers/static — 왜 Folly의 Singleton인가
  55. 55 folly::SingletonVault 분석 — 등록·소멸·의존성
  56. 56 folly::Singleton try_get·try_get_fast — TLS-cached 접근
  57. 57 folly::ExceptionWrapper — type-erased exception holder
  58. 58 folly::ScopeGuard·SCOPE_EXIT — RAII cleanup
  59. 59 folly::Optional vs std::optional
  60. 60 folly::Function vs std::function
  61. 61 folly::Lazy — 지연 초기화 wrapper
  62. 62 folly Meta 스타일 code review 패턴
  63. 63 folly anti-patterns — 잘못 쓰면 std보다 느림
  64. 64 folly vs std 선택 기준 분석
  65. 65 folly::coro 개요 — production C++20 코루틴 어댑터
  66. 66 folly::coro::Task — lazy single-shot 코루틴
  67. 67 folly::coro::AsyncGenerator — 비동기 스트림
  68. 68 folly coro blockingWait·collectAll — 동기 경계와 fan-in
  69. 69 folly::coro::Baton·Mutex — 코루틴-aware 동기화
  70. 70 folly::Expected — 결과 또는 오류
  71. 71 folly::Try — Future 결과 wrapper
  72. 72 folly::Try vs Expected 선택 기준
  73. 73 folly::Range — 일반 iterator pair
  74. 74 folly::Uri — URL 파서
  75. 75 folly Fingerprint64·128 — 분산 hash
  76. 76 folly SpookyHashV2 — fast non-crypto hash
  77. 77 folly::Init — main() 부트스트랩
  78. 78 folly::Indestructible — global lifetime 패턴
  79. 79 folly::MicroLock — 1-byte 락
  80. 80 folly::MicroSpinLock — 가장 좁은 spin lock
  81. 81 folly::format — legacy formatter 분석
  82. 82 folly::demangle — typeid 디망글링
  83. 83 folly::DynamicConverter — dynamic ↔ struct
  84. 84 folly::RecordIO — append-only 로그 파일 포맷
  85. 85 folly::io::Compression — zstd·lz4·snappy wrapper
  86. 86 folly::AsyncIO — io_uring·Linux AIO
  87. 87 folly::CancellationToken — 코루틴·Future 취소 전파
  88. 88 folly::observer — hot config의 atomic refresh
  89. 89 fbcode 패턴 모음 — folly 사용의 실전