mirror of
https://github.com/milvus-io/milvus.git
synced 2026-07-21 10:15:43 +00:00
enhance: loading overhead estimation and LoadingOverheadConfig for caching layer (#48773)
## Summary
- Change \`estimated_byte_size_of_cell\` return semantics from
\`{loaded, total_loading}\` to \`{loaded, loading_overhead}\` across
**all** translators
- Integrate with milvus-common \`LoadingOverheadConfig\` API to cap
total loading overhead per group via \`LoadingOverheadTracker\`
- Add cell-batch loading constants (\`kChannelCapacityMultiplier\`,
\`kLoadingOverheadInflationRatio\`) to \`memory_planner.h\`
- Update milvus-common to \`sparknack/milvus-common\` loading-opt branch
(\`b2efad3\`)
issue: #48772
## Changes
### 1. Overhead semantics change (all translators)
All \`estimated_byte_size_of_cell\` implementations updated so the
second element is pure overhead (temporary buffers during loading), not
total loading cost:
| Translator | mmap overhead | non-mmap overhead |
|-----------|--------------|-------------------|
| ChunkTranslator | \`{2x mem, 1x disk}\` | \`{1x mem, 0}\` |
| DefaultValueChunkTranslator | \`{0, 0}\` | \`{0, 0}\` |
| InterimSealedIndexTranslator | build buffer only | build buffer only |
| SealedIndexTranslator | \`max - final\` | \`max - final\` |
| TextMatchIndexTranslator | temp mem during load | temp disk during
load |
| BsonInvertedIndexTranslator | temp mem (download) | temp disk (local
file) |
| GroupChunkTranslator | via LoadingOverheadConfig | via
LoadingOverheadConfig |
| ManifestGroupTranslator | via LoadingOverheadConfig | via
LoadingOverheadConfig |
### 2. LoadingOverheadConfig for cell-batch translators
\`GroupChunkTranslator\` and \`ManifestGroupTranslator\` set
\`Meta::loading_overhead\` with:
- \`upper_bound\`: \`max_inflight * max_cell_size * inflation_ratio\`
- \`group\`: per-translator-type + CellDataType
This prevents N concurrent column group loads from each reserving full
overhead independently.
### 3. Cell-batch loading constants
Added to \`memory_planner.h\`:
- \`kChannelCapacityMultiplier = 1.5\` — bounded channel capacity
relative to pool size
- \`kLoadingOverheadInflationRatio = 1.2\` — safety margin for overhead
estimation
### 4. milvus-common dependency
Points to \`sparknack/milvus-common\` loading-opt branch (\`b2efad3\`)
which adds:
- \`LoadingOverheadConfig\` struct in \`Translator.h\`
- \`LoadingOverheadTracker\` for per-group overhead capping
- Updated \`CacheSlot\` and \`DList\` to integrate tracker
## Test plan
- [ ] Existing unit tests pass
- [ ] Load collection with multiple column groups, verify reduced
overhead reservation in logs
- [ ] Verify LoadingOverheadTracker metrics show capped overhead
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Shawn Wang <shawn.wang@zilliz.com>
This commit is contained in:
@@ -100,6 +100,15 @@ LoadWithStrategy(const std::vector<std::string>& remote_files,
|
||||
|
||||
// ---- Cell-batch loading ----
|
||||
|
||||
// The channel capacity multiplier relative to pool size.
|
||||
// The bounded channel holds at most (pool_size * kChannelCapacityMultiplier) items,
|
||||
// providing backpressure to producer threads while keeping them busy.
|
||||
constexpr double kChannelCapacityMultiplier = 1.5;
|
||||
|
||||
// Safety margin for loading overhead upper bound estimation.
|
||||
// Covers Arrow Table alignment overhead, metadata, and other estimation errors.
|
||||
constexpr double kLoadingOverheadInflationRatio = 1.2;
|
||||
|
||||
// A cell specification: identifies a cell's location within a specific file.
|
||||
struct CellSpec {
|
||||
int64_t cid; // cell id
|
||||
|
||||
@@ -66,11 +66,11 @@ BsonInvertedIndexTranslator::estimated_byte_size_of_cell(
|
||||
milvus::cachinglayer::cid_t) const {
|
||||
// ignore the cid checking, because there is only one cell
|
||||
if (load_info_.enable_mmap) {
|
||||
return {{0, load_info_.index_size},
|
||||
{load_info_.index_size, load_info_.index_size}};
|
||||
// loaded: on disk; overhead: temp memory for download buffer
|
||||
return {{0, load_info_.index_size}, {load_info_.index_size, 0}};
|
||||
} else {
|
||||
return {{load_info_.index_size, 0},
|
||||
{load_info_.index_size, load_info_.index_size}};
|
||||
// loaded: in memory; overhead: temp disk for local file before loading
|
||||
return {{load_info_.index_size, 0}, {0, load_info_.index_size}};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -140,10 +140,10 @@ ChunkTranslator::estimated_byte_size_of_cell(
|
||||
int64_t memory_size = file_infos_[cid].memory_size;
|
||||
if (use_mmap_) {
|
||||
// For mmap, the memory is counted as disk usage
|
||||
return {{0, memory_size}, {memory_size * 2, memory_size * 2}};
|
||||
return {{0, memory_size}, {memory_size * 2, memory_size}};
|
||||
} else {
|
||||
// For non-mmap, the memory is counted as memory usage
|
||||
return {{memory_size, 0}, {memory_size * 2, 0}};
|
||||
return {{memory_size, 0}, {memory_size, 0}};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,9 +214,9 @@ DefaultValueChunkTranslator::estimated_byte_size_of_cell(
|
||||
auto rows = rows_end - rows_begin;
|
||||
auto cell_bytes = value_size * rows;
|
||||
if (use_mmap_) {
|
||||
return {{0, cell_bytes}, {0, cell_bytes}};
|
||||
return {{0, cell_bytes}, {0, 0}};
|
||||
} else {
|
||||
return {{cell_bytes, 0}, {cell_bytes, 0}};
|
||||
return {{cell_bytes, 0}, {0, 0}};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -96,8 +96,7 @@ InterimSealedIndexTranslator::estimated_byte_size_of_cell(
|
||||
} else if (index_type_ == knowhere::IndexEnum::INDEX_FAISS_IVFFLAT_CC) {
|
||||
// fp16/bf16 all use float32 to build index
|
||||
int64_t fp32_size = row_count * sizeof(float) * dim_;
|
||||
return {{fp32_size, 0},
|
||||
{static_cast<int64_t>(fp32_size + fp32_size * 0.5), 0}};
|
||||
return {{fp32_size, 0}, {static_cast<int64_t>(fp32_size * 0.5), 0}};
|
||||
} else {
|
||||
// SPARSE_WAND_CC and SPARSE_INVERTED_INDEX_CC basically has the same size as the
|
||||
// raw data.
|
||||
|
||||
@@ -100,8 +100,9 @@ SealedIndexTranslator::estimated_byte_size_of_cell(
|
||||
// this is an estimation, error could be up to 20%.
|
||||
return {milvus::cachinglayer::ResourceUsage(request.final_memory_cost,
|
||||
request.final_disk_cost),
|
||||
milvus::cachinglayer::ResourceUsage(request.max_memory_cost,
|
||||
request.max_disk_cost * 2)};
|
||||
milvus::cachinglayer::ResourceUsage(
|
||||
request.max_memory_cost - request.final_memory_cost,
|
||||
request.max_disk_cost * 2 - request.final_disk_cost)};
|
||||
}
|
||||
|
||||
const std::string&
|
||||
|
||||
@@ -67,14 +67,12 @@ TextMatchIndexTranslator::estimated_byte_size_of_cell(
|
||||
milvus::cachinglayer::cid_t) const {
|
||||
// ignore the cid checking, because there is only one cell
|
||||
if (load_info_.enable_mmap) {
|
||||
return {{0, load_info_.index_size},
|
||||
{load_info_.index_size, load_info_.index_size}};
|
||||
return {{0, load_info_.index_size}, {load_info_.index_size, 0}};
|
||||
} else {
|
||||
// The reason the maximum disk usage is not zero is that the text match index
|
||||
// is first written to the disk, then loaded into memory. Only after that are
|
||||
// the disk files deleted.
|
||||
return {{load_info_.index_size, 0},
|
||||
{load_info_.index_size, load_info_.index_size}};
|
||||
return {{load_info_.index_size, 0}, {0, load_info_.index_size}};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -231,6 +231,37 @@ GroupChunkTranslator::GroupChunkTranslator(
|
||||
total_row_groups,
|
||||
num_cells,
|
||||
kRowGroupsPerCell);
|
||||
|
||||
// Set loading overhead config to cap total overhead reservation.
|
||||
// During get_cells, decoded Arrow Tables exist simultaneously in:
|
||||
// - pool threads (pool_size): reading batches, pending push
|
||||
// - bounded channel (pool_size * kChannelCapacityMultiplier): pushed, awaiting pop
|
||||
// - main thread (1): being converted to GroupChunk
|
||||
if (!meta_.chunk_memory_size_.empty()) {
|
||||
// Use THREAD_POOL_MAX_THREADS_SIZE as the upper bound for pool size.
|
||||
// This is the global cap applied to all priority pools.
|
||||
int pool_size = milvus::THREAD_POOL_MAX_THREADS_SIZE.load();
|
||||
if (pool_size <= 0) {
|
||||
pool_size = static_cast<int>(std::round(
|
||||
milvus::CPU_NUM *
|
||||
milvus::HIGH_PRIORITY_THREAD_CORE_COEFFICIENT.load()));
|
||||
}
|
||||
auto max_inflight = static_cast<int64_t>(
|
||||
pool_size * (1.0 + kChannelCapacityMultiplier) + 1);
|
||||
int64_t max_cell_sz = *std::max_element(
|
||||
meta_.chunk_memory_size_.begin(), meta_.chunk_memory_size_.end());
|
||||
auto ub = static_cast<int64_t>(max_inflight * max_cell_sz *
|
||||
kLoadingOverheadInflationRatio);
|
||||
auto upper_bound = use_mmap_
|
||||
? milvus::cachinglayer::ResourceUsage{ub, ub}
|
||||
: milvus::cachinglayer::ResourceUsage{ub, 0};
|
||||
// Group by CellDataType name so all CacheSlots of the same type
|
||||
// share one overhead upper bound via LoadingOverheadTracker.
|
||||
auto group = fmt::format("GroupChunkTranslator_{}",
|
||||
static_cast<int>(meta_.cell_data_type));
|
||||
meta_.loading_overhead =
|
||||
milvus::cachinglayer::LoadingOverheadConfig{upper_bound, group};
|
||||
}
|
||||
}
|
||||
|
||||
GroupChunkTranslator::~GroupChunkTranslator() {
|
||||
@@ -352,7 +383,8 @@ GroupChunkTranslator::get_cells(milvus::OpContext* ctx,
|
||||
auto& pool = milvus::ThreadPools::GetThreadPool(
|
||||
milvus::PriorityForLoad(load_priority_));
|
||||
auto channel = std::make_shared<milvus::segcore::CellReaderChannel>(
|
||||
static_cast<size_t>(pool.GetMaxThreadNum() * 1.5));
|
||||
static_cast<size_t>(pool.GetMaxThreadNum() *
|
||||
milvus::segcore::kChannelCapacityMultiplier));
|
||||
auto fs = milvus_storage::ArrowFileSystemSingleton::GetInstance()
|
||||
.GetArrowFileSystem();
|
||||
|
||||
|
||||
@@ -170,6 +170,33 @@ ManifestGroupTranslator::ManifestGroupTranslator(
|
||||
total_row_groups,
|
||||
num_cells,
|
||||
kRowGroupsPerCell);
|
||||
|
||||
// Set loading overhead config to cap total overhead reservation.
|
||||
if (!meta_.chunk_memory_size_.empty()) {
|
||||
// Use THREAD_POOL_MAX_THREADS_SIZE as the upper bound for pool size.
|
||||
// This is the global cap applied to all priority pools.
|
||||
int pool_size = milvus::THREAD_POOL_MAX_THREADS_SIZE.load();
|
||||
if (pool_size <= 0) {
|
||||
pool_size = static_cast<int>(std::round(
|
||||
milvus::CPU_NUM *
|
||||
milvus::HIGH_PRIORITY_THREAD_CORE_COEFFICIENT.load()));
|
||||
}
|
||||
auto max_inflight = static_cast<int64_t>(
|
||||
pool_size * (1.0 + kChannelCapacityMultiplier) + 1);
|
||||
int64_t max_cell_sz = *std::max_element(
|
||||
meta_.chunk_memory_size_.begin(), meta_.chunk_memory_size_.end());
|
||||
auto ub = static_cast<int64_t>(max_inflight * max_cell_sz *
|
||||
kLoadingOverheadInflationRatio);
|
||||
auto upper_bound = use_mmap_
|
||||
? milvus::cachinglayer::ResourceUsage{ub, ub}
|
||||
: milvus::cachinglayer::ResourceUsage{ub, 0};
|
||||
// Group by CellDataType name so all CacheSlots of the same type
|
||||
// share one overhead upper bound via LoadingOverheadTracker.
|
||||
auto group = fmt::format("ManifestGroupTranslator_{}",
|
||||
static_cast<int>(meta_.cell_data_type));
|
||||
meta_.loading_overhead =
|
||||
milvus::cachinglayer::LoadingOverheadConfig{upper_bound, group};
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
@@ -246,7 +273,8 @@ ManifestGroupTranslator::get_cells(
|
||||
auto& pool = milvus::ThreadPools::GetThreadPool(
|
||||
milvus::PriorityForLoad(load_priority_));
|
||||
auto channel = std::make_shared<milvus::segcore::CellReaderChannel>(
|
||||
static_cast<size_t>(pool.GetMaxThreadNum() * 1.5));
|
||||
static_cast<size_t>(pool.GetMaxThreadNum() *
|
||||
milvus::segcore::kChannelCapacityMultiplier));
|
||||
|
||||
auto load_futures =
|
||||
milvus::segcore::LoadCellBatchAsync(ctx,
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
|
||||
milvus_add_pkg_config("milvus-common")
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES "")
|
||||
set( MILVUS-COMMON-VERSION 5983a18 )
|
||||
set( MILVUS-COMMON-VERSION 2eb54ae )
|
||||
set( GIT_REPOSITORY "https://github.com/zilliztech/milvus-common.git")
|
||||
|
||||
message(STATUS "milvus-common repo: ${GIT_REPOSITORY}")
|
||||
|
||||
Reference in New Issue
Block a user