absl::InlinedVector — small buffer optimization
#한 줄 요약
absl::InlinedVector<T, N>은 N개까지는 객체 내부 inline buffer에 저장하고, 이를 넘으면 자동으로 heap으로 전환되는 vector다. std::vector의 인터페이스를 그대로 두면서 흔한 작은 크기에서 alloc을 피한다. small-buffer optimization(SBO)의 표준 구현.
#동기
std::vector는 항상 heap을 쓴다. 빈 vector는 alloc이 없을 수 있지만 첫 push_back에서 alloc이 일어난다. 그러나 많은 워크로드의 vector는 짧다.
- HTTP 헤더 컬렉션 (대부분 ~10개)
- 함수 인자 리스트 (대부분 ~4개)
- DOM tree의 children (대부분
03개) - log entry의 fields (대부분 ~5개)
FixedArray는 크기 변경이 안 된다. vector는 alloc이 강제다. 그 사이가 InlinedVector다.
작은 경우와 큰 경우의 저장 위치를 그림으로 보면 다음과 같다.
absl::InlinedVector<int, 4> v;v.push_back(1); v.push_back(2); v.push_back(3);// 여기까지 alloc 없음 — inline buffer 안
v.push_back(4); v.push_back(5);// 5번째에서 heap 전환#API와 사용법
#include "absl/container/inlined_vector.h"
namespace absl {template <typename T, size_t N, typename A = std::allocator<T>>class InlinedVector { public: // std::vector와 동일 인터페이스 (대부분) void push_back(const T& v); void push_back(T&& v); template <typename... Args> void emplace_back(Args&&... args); void pop_back(); iterator insert(const_iterator pos, const T& v); iterator erase(const_iterator pos); void clear(); void resize(size_t n); void reserve(size_t n);
T& operator[](size_t i); T* data(); size_t size() const; size_t capacity() const; bool empty() const;};}std::vector의 drop-in 가까운 인터페이스. 대부분의 코드 변경은 typedef 하나.
// beforestd::vector<int> v;// afterabsl::InlinedVector<int, 4> v;#내부 구현
InlinedVector는 union으로 두 상태를 표현한다.
// absl/container/inlined_vector.h (요약, internal 구조)template <typename T, size_t N, typename A>class InlinedVector { union Storage { struct { T* data; size_t capacity; } heap; alignas(T) char inline_[N * sizeof(T)]; };
Storage storage_; size_t size_; bool is_inline_; // 실제로는 tag bit가 size_/capacity에 packed};is_inline_이 true면 inline_ buffer 사용, false면 heap.data. 객체의 sizeof는 다음과 같다.
- N = 4, T = int: ~24 바이트 (inline 4*4 + size + tag).
- inline buffer가 클수록 객체 자체가 커진다.
heap 전환은 capacity()가 N을 초과할 때 일어난다. 한 번 heap이 되면 다시 inline으로 돌아가지 않는다.
v.reserve(100); // heap 전환v.clear(); // size=0, 그러나 여전히 heapv.shrink_to_fit();// heap 해제, inline으로 복귀 (size ≤ N이면)#std::vector / FixedArray 비교
| 항목 | std::vector | FixedArray | InlinedVector |
|---|---|---|---|
| 크기 변경 | O | X | O |
| 첫 alloc | 첫 push 시 | 생성 시 (큰 N) | 크기 초과 시 |
| 작은 경우 alloc | 1회 | 0 | 0 |
| 객체 sizeof | 3 포인터 | N*sizeof(T) + 헤더 | N*sizeof(T) + 헤더 |
| std API 호환 | full | 부분 (resize 없음) | 대부분 |
#코드 리뷰 포인트
1. push_back 평균이 작은 vector
profiling으로 평균 크기가 N 이하인 vector를 식별 → InlinedVector<T, N>. 작은 워크로드에서 alloc을 0으로 만든다.
// 회피std::vector<Field> fields;for (...) fields.push_back(...); // 대부분 alloc
// Goodabsl::InlinedVector<Field, 8> fields;for (...) fields.push_back(...); // 8개 이하면 alloc 02. struct 멤버
struct HttpRequest { // 대부분 헤더가 ~10개 absl::InlinedVector<Header, 12> headers;};객체 자체가 커지는 트레이드오프. sizeof(HttpRequest)가 늘어나니 cache 영향 인지.
3. 매개변수 전달
InlinedVector는 복사 가능하나 이동 비용이 N에 비례한다. 큰 N + 자주 이동이면 비효율적.
// 회피 — N=64 + 함수 호출마다 이동void Process(absl::InlinedVector<int, 64> v);
// Goodvoid Process(absl::Span<const int> v); // view로 받기#안티패턴
너무 큰 N
absl::InlinedVector<BigStruct, 100> v;// sizeof(v) ≥ 100 * sizeof(BigStruct)// vector를 멤버로 갖는 객체가 모두 커진다inline buffer는 객체 sizeof에 직접 들어간다. N은 흔한 작은 크기로.
heap 전환 후 빈번 clear
absl::InlinedVector<int, 4> v;v.reserve(1000); // heapfor (...) { v.clear(); // size=0이지만 capacity=1000 (heap) // 사용}inline 복귀 의도라면 shrink_to_fit. 그러나 매번 shrink_to_fit이 빈번한 alloc/dealloc을 부른다. 패턴을 다시 본다.
move semantics 가정
std::vector move는 포인터 swap이라 O(1)이다. InlinedVector가 inline 상태에서 move하면 각 원소를 개별 move해야 한다 — O(N). hot path move가 잦으면 신중히.
#정리
InlinedVector<T, N>은 N 이하면 inline buffer, 초과면 heap.std::vector인터페이스 호환.- 평균 크기가 작은 vector의 alloc을 0으로 줄인다.
- 객체 sizeof가 inline buffer만큼 커진다.
- 큰 N + 자주 move는 비효율.
#다음 편
Part 5-07 — Swiss Table internals에서 flat_hash_map/flat_hash_set의 내부 구현을 자세히 본다.
#관련 항목
Abseil Code Review · 33 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 algorithm container 확장 — c_sort·c_find_if·c_count_if
absl::c_* algorithm wrapper — container 전체를 받아 begin/end 자동 처리, STL algorithm의 한 줄 boilerplate를 제거.
같은 시리즈에서 이어 읽기
absl::FixedArray — 런타임 크기 stack 배열
Part 5-05: absl::FixedArray — 런타임 결정 크기지만 작으면 stack, 크면 heap. VLA의 안전한 대체.
같은 시리즈에서 이어 읽기
absl::btree_map — sorted·cache-friendly B-tree
Part 5-04: absl::btree_map — std::map(red-black tree)의 B-tree 대체, cache locality와 메모리 효율, sorted 컨테이너의 새 기준.
같은 시리즈에서 이어 읽기