FixedDestMultiMove
Move Type: Fixed Complexity: O(bundles × bundle_size) with parallel evaluation Primary Use: RAS stackable solve
Move bundles of related objects from hot container to a specific fixed destination. The multi-object version of FixedDest, designed for RAS local search.
Overview
FixedDestMultiMove (also known as FIXED_DEST_MULTIPLE) evaluates moving bundles of related objects from the hot container to a single predetermined destination container. Unlike FixedDest which moves one object at a time, this move type moves entire object bundles together.
Use when:
- Using RAS stackable solve
- Moving bundles of related objects together
- Know exactly which destination to target
- Objects must move as coordinated groups
- Want parallel evaluation of move sets
Avoid when:
- Not using RAS local search
- Objects can move independently (use FixedDest)
- Don't know destination (use Single)
- Need to explore multiple destinations
- Want solver to find best destination
Quick Example
- Python
- C++
# Move object bundles from hot container to specific destination
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedDestMultiMoveTypeSpec(
specialContainer="new_server", # Fixed destination
),
]
)
)
)
void quickExample() {
// Move object bundles from hot container to specific destination
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
solver.setObjectName("task");
solver.setContainerName("server");
LocalSearchSolverSpec solverSpec;
FixedDestMultiMoveTypeSpec fixedDestMultiSpec;
fixedDestMultiSpec.specialContainer() = "new_server"; // Fixed destination
// RasLocalSearchMetadata is required for multi-move types
auto rasMetadata = std::make_shared<const RasLocalSearchMetadata>();
fixedDestMultiSpec.rasLocalSearchMetadata() = std::move(rasMetadata);
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedDestMultiMoveTypeSpec() =
fixedDestMultiSpec;
solver.addSolver(solverSpec);
// Setup problem
solver.setAssignment(
std::map<std::string, std::vector<std::string>>{
{"server0", {"task0", "task1", "task2"}},
{"server1", {"task3"}},
{"new_server", {}},
});
solver.addObjectDimension(
"cpu",
std::map<std::string, double>{
{"task0", 1.0},
{"task1", 1.0},
{"task2", 1.0},
{"task3", 3.0},
});
BalanceSpec balanceSpec;
balanceSpec.name() = "balance-cpu";
balanceSpec.scope() = "server";
balanceSpec.dimension() = "cpu";
solver.addGoal(balanceSpec, 1.0);
// Note: solve() is not called here because multi-move types require
// additional bundle/partition setup (e.g., search space partitioning)
// that is beyond the scope of this basic example.
}
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
specialContainer | string | Yes | null | Fixed destination container name |
maxSamplesPerEquivSet | int | No | 5 | Max move sets to consider per equivalent set |
rasLocalSearchMetadata | RasLocalSearchMetadata | No | null | Metadata for RAS local search |
Parameter Details
specialContainer:
- Name of the specific destination container
- All object bundles will target this container only
- Must be a valid container name
maxSamplesPerEquivSet:
- Limits move sets evaluated per equivalent set
- Equivalent sets are groups identical from local search perspective
- Higher values = better quality, more computation
- Default: 5 samples per equivalent set
rasLocalSearchMetadata:
- Metadata specific to RAS stackable solve
- Optional configuration for RAS local search behavior
- Only relevant for RAS use cases
How It Works
Given a hot container (most broken):
- Select bundle: Pick object bundle from hot container
- Evaluate move: Test moving all objects in bundle to
specialContainer - Parallel evaluation: All move sets evaluated in parallel (multi-threading)
- Sample: Consider up to
maxSamplesPerEquivSetper equivalent set - Repeat: Try all bundles in hot container
- Apply best: Apply the bundle move that improves objective most
Visual Example
Before move: After bundle move to specialContainer:
┌──────────────┐ ┌──────────────┐
│ Hot │ │ Hot │
│ Container │ ───────────> │ Container │
│ Bundle1 ────┼──┐ obj1, obj2 │ Bundle2 │
│ • obj1 │ │ │ • obj3 │
│ • obj2 ────┼──┘ │ • obj4 │
│ Bundle2 │ └──────────────┘
│ • obj3 │
│ • obj4 │ ┌──────────────┐
└──────────────┘ │ Special │
│ Container │
┌──────────────┐ │ • objA │
│ Special │ │ • objB │
│ Container │ │ Bundle1 ←───┼── Entire bundle moved!
│ • objA │ │ • obj1 │
│ • objB <───┼──┐ │ • obj2 │
└──────────────┘ └─ Bundle dest └──────────────┘
Entire bundle moves together to specialContainer
Comparison with FixedDest
| Aspect | FixedDest | FixedDestMultiMove |
|---|---|---|
| Move unit | Single object | Bundle of objects |
| Use case | General single moves | RAS stackable solve |
| Evaluation | Sequential | Parallel |
| Complexity | O(N) | O(B × S) where B=bundles, S=bundle_size |
| Coordination | Independent objects | Related object groups |
Complexity
Per iteration: O(B × S)
Where:
- B = number of bundles in hot container
- S = average bundle size (objects per bundle)
Sampling: Limited by maxSamplesPerEquivSet per equivalent set
Example - RAS stackable:
- Hot container bundles: 20
- Average bundle size: 4 objects
- maxSamplesPerEquivSet: 5
- Evaluations: ~20 bundles (limited by sampling), 4 objects each
- Benefit: Parallel evaluation across bundles
Usage Patterns
RAS Migration
Move RAS bundles to new server:
- Python
- C++
# Migrate RAS task bundles to new server
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedDestMultiMoveTypeSpec(
specialContainer="new_ras_server", # New server
),
]
)
)
)
void rasMigration() {
// Migrate RAS task bundles to new server
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
solver.setObjectName("task");
solver.setContainerName("server");
LocalSearchSolverSpec solverSpec;
FixedDestMultiMoveTypeSpec fixedDestMultiSpec;
fixedDestMultiSpec.specialContainer() = "new_ras_server"; // New server
auto rasMetadata = std::make_shared<const RasLocalSearchMetadata>();
fixedDestMultiSpec.rasLocalSearchMetadata() = std::move(rasMetadata);
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedDestMultiMoveTypeSpec() =
fixedDestMultiSpec;
solver.addSolver(solverSpec);
// Setup RAS problem
solver.setAssignment(
std::map<std::string, std::vector<std::string>>{
{"server0", {"task0", "task1", "task2", "task3"}},
{"server1", {"task4", "task5"}},
{"new_ras_server", {}},
});
solver.addObjectDimension(
"memory",
std::map<std::string, double>{
{"task0", 2.0},
{"task1", 2.0},
{"task2", 1.5},
{"task3", 1.5},
{"task4", 3.0},
{"task5", 2.0},
});
BalanceSpec balanceSpec;
balanceSpec.name() = "balance-memory";
balanceSpec.scope() = "server";
balanceSpec.dimension() = "memory";
solver.addGoal(std::move(balanceSpec), 1.0);
// Note: solve() is not called here because multi-move types require
// additional bundle/partition setup (e.g., search space partitioning)
// that is beyond the scope of this basic example.
}
Fill Specific Container
Target specific underutilized container with bundles:
- Python
- C++
# Fill specific underutilized container with object bundles
solver = ProblemSolver(service_name="example", service_scope="test")
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedDestMultiMoveTypeSpec(
specialContainer="underutilized_server",
),
]
)
)
)
void fillContainer() {
// Fill specific underutilized container with object bundles
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
LocalSearchSolverSpec solverSpec;
FixedDestMultiMoveTypeSpec fixedDestMultiSpec;
fixedDestMultiSpec.specialContainer() = "underutilized_server";
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedDestMultiMoveTypeSpec() =
fixedDestMultiSpec;
solver.addSolver(solverSpec);
}
Sampling Control
Control sampling of equivalent sets:
- Python
- C++
# Limit evaluations by sampling equivalent sets
solver = ProblemSolver(service_name="example", service_scope="test")
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedDestMultiMoveTypeSpec(
specialContainer="destination_server",
maxSamplesPerEquivSet=10, # Evaluate up to 10 per equiv set
),
]
)
)
)
void sampling() {
// Limit evaluations by sampling equivalent sets
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
LocalSearchSolverSpec solverSpec;
FixedDestMultiMoveTypeSpec fixedDestMultiSpec;
fixedDestMultiSpec.specialContainer() = "destination_server";
fixedDestMultiSpec.maxSamplesPerEquivSet() =
10; // Evaluate up to 10 per equiv set
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedDestMultiMoveTypeSpec() =
fixedDestMultiSpec;
solver.addSolver(solverSpec);
}
With RAS Metadata
Configure with RAS local search metadata:
- Python
- C++
# RAS stackable solve with metadata
solver = ProblemSolver(service_name="example", service_scope="test")
from rebalancer.interface.thrift.v2.SolverSpecs.thrift_types import (
RasLocalSearchMetadata,
)
solver.addSolver(
SolverSpecs(
localSearchSolverSpec=LocalSearchSolverSpec(
moveTypeList=[
FixedDestMultiMoveTypeSpec(
specialContainer="ras_destination",
maxSamplesPerEquivSet=5,
rasLocalSearchMetadata=RasLocalSearchMetadata(),
),
]
)
)
)
void rasMetadata() {
// RAS stackable solve with metadata
auto executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
ProblemSolver solver(executor, "example", "test");
LocalSearchSolverSpec solverSpec;
auto rasMetadata = std::make_shared<const RasLocalSearchMetadata>();
// Configure RAS metadata as needed
FixedDestMultiMoveTypeSpec fixedDestMultiSpec;
fixedDestMultiSpec.specialContainer() = "ras_destination";
fixedDestMultiSpec.maxSamplesPerEquivSet() = 5;
fixedDestMultiSpec.rasLocalSearchMetadata() = std::move(rasMetadata);
solverSpec.moveTypeList()->emplace_back();
solverSpec.moveTypeList()->back().fixedDestMultiMoveTypeSpec() =
fixedDestMultiSpec;
solver.addSolver(solverSpec);
}
Performance Characteristics
Speedup Analysis
| Bundles | Bundle Size | FixedDestMultiMove | Single | Speedup |
|---|---|---|---|---|
| 20 | 4 | 80 | 100K | ~1250× |
| 50 | 8 | 400 | 1M | ~2500× |
| 100 | 10 | 1K | 10M | ~10000× |
Benefits:
- Parallel evaluation: All move sets evaluated concurrently
- Bundle efficiency: Move related objects together
- Sampling control:
maxSamplesPerEquivSetlimits computation - Fixed destination: No destination exploration overhead
When Does It Help?
FixedDestMultiMove helps when:
- RAS stackable solve: Designed for RAS local search
- Bundle coordination: Objects must move together
- Known destination: Exactly where bundles should go
- Parallel benefits: Can leverage multi-threading
- Large bundles: Moving groups is more efficient than individuals
FixedDestMultiMove does NOT help when:
- Not RAS: Use FixedDest for single objects
- Independent objects: Objects can move separately
- Unknown destination: Need solver to find best destination
- Exploring options: Want to try multiple destinations
Comparison with Variants
| Move Type | Destination | Source | Move Unit | Use Case |
|---|---|---|---|---|
| FixedDest | Fixed specific | Hot container | Single object | General moves |
| FixedDestMultiMove | Fixed specific | Hot container | Object bundle | RAS stackable |
| FixedSourceMultiMove | Multiple dests | Fixed specific | Object bundle | RAS pull from source |
| FixedDestSwapMultiMove | Fixed specific | Hot container | Bundle swap | RAS swaps |
Decision tree:
- RAS stackable + know dest? → FixedDestMultiMove
- RAS stackable + know source? → FixedSourceMultiMove
- Single objects + know dest? → FixedDest
- Neither? → Single
Troubleshooting
Problem: No improving moves found
Diagnosis: Bundles can't beneficially move to special container
Solutions:
- Verify
specialContaineris correct destination - Check capacity constraints on special container
- Bundle sizes may be too large for destination
- May already be optimal for this destination
- Try different destination or Single
Problem: Wrong bundles moving
Diagnosis: Bundle formation or objective function issue
Solutions:
- Verify bundle formation logic is correct
- Check objective function rewards correct bundle moves
- Review equivalent set definitions
- May need different objective or constraints
- Examine which bundles the solver is selecting
Problem: Too many evaluations
Diagnosis: Too many bundles or large equivalent sets
Solutions:
- Reduce
maxSamplesPerEquivSet(default: 5) - Start with smaller sample (e.g., 2-3 per set)
- Review bundle formation to reduce bundle count
- Check if bundles can be simplified
Problem: Missing good bundle moves
Diagnosis: maxSamplesPerEquivSet too small, sampling misses best bundles
Solutions:
- Increase
maxSamplesPerEquivSet(e.g., 10, 20) - Review equivalent set definitions
- May need to evaluate all bundles (remove sampling)
- Run multiple times with different random seeds
When to Use FixedDestMultiMove
DO use when:
- Using RAS stackable solve
- Moving bundles of related objects
- Know exactly which destination to target
- Objects must move as coordinated groups
- Want to benefit from parallel evaluation
DO NOT use when:
- Not using RAS local search
- Objects can move independently (use FixedDest)
- Don't know destination
- Need to explore multiple destinations
- General optimization (use Single)
Related Move Types
Fixed bundle variants:
- FixedDest - Single object to fixed dest
- FixedDestMultiMove - Bundle to fixed dest (this)
- FixedSourceMultiMove - Bundle from fixed source
- FixedDestSwapMultiMove - Bundle swaps to fixed dest
RAS move types:
- All three above are used exclusively for RAS stackable solve
- Work together in RAS local search strategies
General alternatives:
Source Code
- Thrift definition:
interface/thrift/SolverSpecs.thrift:596 - Implementation:
solver/moves/FixedDestMultiMoveType.h - Tests:
solver/moves/tests/
Next Steps
- Learn about FixedSourceMultiMove for pulling bundles from specific sources
- Try FixedDestSwapMultiMove for bundle swaps
- Review FixedDest for single-object moves
- See Move Types Overview for choosing move types