Skip to content

Add memtable MultiGet finger search optimization#14428

Open
anand1976 wants to merge 4 commits intofacebook:mainfrom
anand1976:memtable_mget
Open

Add memtable MultiGet finger search optimization#14428
anand1976 wants to merge 4 commits intofacebook:mainfrom
anand1976:memtable_mget

Conversation

@anand1976
Copy link
Contributor

@anand1976 anand1976 commented Mar 5, 2026

Add memtable batch lookup optimization with finger search

Summary

Optimize memtable MultiGet by using a finger search on the skip list. After finding key[i], the search path (Splice) is retained as a "finger" for key[i+1]. The next search walks up the finger until the forward pointer overshoots, then descends -- costing O(log d) where d is the distance between consecutive sorted keys, rather than O(log N) from the head each time.

Controlled by the new memtable_batch_lookup_optimization column family option (default: false).

Changes

  • Add FindGreaterOrEqualWithFinger() and MultiGet() to InlineSkipList with optional paranoid validation support (key ordering checks and per-key checksum verification via default parameters)
  • Add virtual MultiGet() to MemTableRep, override in SkipListRep
  • Add memtable_batch_lookup_optimization CF option
  • Integrate finger search into MemTable::MultiGet -- sorts keys, performs batched finger search, then unscrambles results
  • Add db_bench, db_stress, and crash test support
  • Add unit tests for InlineSkipList::MultiGet (5 tests) and integration tests at the DB level (25 BatchLookup tests), including paranoid validation tests

Benchmark Results

Setup: 2M keys in memtable, release build (DEBUG_LEVEL=0).

Single-threaded multireadrandom

Batch Size Baseline BatchOpt vs Base
2 363,593 376,562 +3.6%
8 385,204 386,082 +0.2%
32 360,339 375,105 +4.1%
64 352,696 378,497 +7.3%

Multithreaded multireadrandom (batch_size=64)

Threads Baseline BatchOpt vs Base
4 171,356 185,633 +8.3%
8 161,478 171,392 +6.1%

multireadwhilewriting (batch_size=64)

Threads Baseline BatchOpt vs Base
1 163,938 175,068 +6.8%
4 109,698 120,790 +10.1%
8 116,623 123,658 +6.0%

No regression at any batch size or thread count. The finger is stack-allocated per MultiGet call, so there is no shared state or cache-line contention between threads. Concurrent writes don't invalidate the finger's bracket since it only reads via acquire-load Next() pointers.

Test Plan

  • make check -- all tests pass
  • ASSERT_STATUS_CHECKED=1 make check -- all tests pass
  • 2-hour db_crashtest.py blackbox with --memtable_batch_lookup_optimization=1 -- passed
  • 2-hour db_crashtest.py blackbox with --memtable_batch_lookup_optimization=1 --paranoid_memory_checks=1 -- passed

Implement finger search on skip list for memtable MultiGet. After each
key lookup, the search path (splice) is cached and reused for the next
key, reducing per-key cost from O(log N) to O(log d) where d is the
distance between consecutive keys.

Changes:
- Add FindGreaterOrEqualWithFinger() and MultiGet() to InlineSkipList
- Add virtual MultiGet() to MemTableRep, override in SkipListRep
- Add memtable_multi_get_finger_search CF option (default: false)
- Integrate finger search into MemTable::MultiGet when enabled
- Add db_bench, db_stress, and crash test support
- Add unit tests (InlineSkipList level) and integration tests (DB level)

Benchmark results (2M keys in memtable, batch_size=64):
- Baseline: ~265,700 ops/sec
- Finger search: ~283,800 ops/sec
- Improvement: ~7%
@anand1976 anand1976 added the WIP Work in progress label Mar 5, 2026
@meta-cla meta-cla bot added the CLA Signed label Mar 5, 2026
Optimize MultiGet by using a finger search on the memtable skip list.
After finding key[i], the search path (Splice) is retained as a
"finger" for key[i+1]. The next search walks up the finger until the
forward pointer overshoots, then descends — costing O(log d) where
d is the distance between consecutive sorted keys, rather than
O(log n) from the head each time.

Key changes:
- Add InlineSkipList::FindGreaterOrEqualWithFinger() and MultiGet()
  with optional paranoid validation parameters (default off)
- Add MemTableRep::MultiGet() virtual with validation support
- Add SkipListRep::MultiGet() override delegating to skip list
- Add MemTable::MultiGet() batched lookup phase that sorts keys,
  performs finger search, then unscrambles results
- Rename option from memtable_multi_get_finger_search to
  memtable_batch_lookup_optimization
- Add db_bench, db_stress, and crash test support
- Add comprehensive unit tests including paranoid validation tests

The optimization provides +3.6% to +10.1% throughput improvement
on MultiGet benchmarks across various configurations.
Fix ASSERT_STATUS_CHECKED failure: wrap all list.MultiGet() calls
in inlineskiplist_test.cc with ASSERT_OK() to check the returned
Status, which is now non-void after the function deduplication.
@facebook facebook deleted a comment from github-actions bot Mar 9, 2026
@anand1976 anand1976 requested a review from pdillinger March 9, 2026 16:47
@anand1976 anand1976 removed the WIP Work in progress label Mar 9, 2026
@meta-codesync
Copy link

meta-codesync bot commented Mar 9, 2026

@anand1976 has imported this pull request. If you are a Meta employee, you can view this in D95813786.

@anand1976 anand1976 requested a review from xingbowang March 9, 2026 17:38
- Value-initialize std::array declarations in memtable.cc to fix
  cppcoreguidelines-pro-type-member-init warnings
- Add reserve() before push_back loop in inlineskiplist_test.cc to
  fix performance-inefficient-vector-operation warning
- Add braces around single-statement for loop in db_basic_test.cc
  to fix readability-braces-around-statements warning
@github-actions
Copy link

github-actions bot commented Mar 9, 2026

⚠️ clang-tidy: 2 warning(s) on changed lines

Completed in 994.8s.

Summary by check

Check Count
cert-err58-cpp 1
concurrency-mt-unsafe 1
Total 2

Details

db_stress_tool/db_stress_tool.cc (1 warning(s))
db_stress_tool/db_stress_tool.cc:279:7: warning: function is not thread safe [concurrency-mt-unsafe]
tools/ldb_cmd.cc (1 warning(s))
tools/ldb_cmd.cc:91:31: warning: initialization of 'ARG_UNIFORM_CV_THRESHOLD' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]

@meta-codesync
Copy link

meta-codesync bot commented Mar 10, 2026

@anand1976 has imported this pull request. If you are a Meta employee, you can view this in D95813786.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant