6.9 KiB
6.9 KiB
swarm
Docker Swarm distributed training for heterogeneous CPU clusters.
Directory: swarm/
Status: Complete with 14 passing tests
Overview
The swarm module provides distributed training across heterogeneous hardware using a parameter server architecture:
- Coordinator: Holds master weights, aggregates gradients, distributes updates
- Workers: Compute forward/backward passes, send compressed gradients
Key features:
- Async gradient updates (workers don't block each other)
- TopK gradient compression (90% bandwidth reduction)
- FP16 compression option
- Hardware profile auto-detection
- Fault tolerance (workers can drop and rejoin)
- Docker Swarm deployment support
Architecture
┌─────────────────┐
│ Coordinator │
│ (holds weights)│
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Worker │ │ Worker │ │ Worker │
│ (Pi 4) │ │ (Laptop)│ │ (Server)│
└─────────┘ └─────────┘ └─────────┘
Quick Start
Running Locally
# Terminal 1: Start coordinator
./bin/swarm_coordinator --port 7100
# Terminal 2: Start worker
./bin/swarm_worker --host localhost --port 7100
Docker Swarm Deployment
cd swarm/docker
./deploy.sh
Files
| File | Description |
|---|---|
protocol.h/c |
Wire protocol, compression, message handling |
network.h/c |
TCP socket abstraction |
config.h/c |
TOML-like config file parsing |
coordinator.h/c |
Parameter server |
worker.h/c |
Training worker |
coordinator_main.c |
Coordinator CLI entry point |
worker_main.c |
Worker CLI entry point |
Protocol
Message Types
| Type | Direction | Description |
|---|---|---|
REGISTER |
Worker → Coord | Worker registration |
REGISTER_ACK |
Coord → Worker | Registration confirmed |
WEIGHTS |
Coord → Worker | Full weight tensor |
GRADIENTS |
Worker → Coord | Compressed gradients |
UPDATE |
Coord → Worker | Weight update delta |
START |
Coord → Worker | Start training |
PAUSE/RESUME |
Coord → Worker | Training control |
SHUTDOWN |
Coord → Worker | Graceful shutdown |
HEARTBEAT |
Both | Connection keepalive |
CHECKPOINT |
Coord → Worker | Save checkpoint |
Compression
| Type | Ratio | Description |
|---|---|---|
NONE |
1:1 | No compression |
FP16 |
2:1 | Float16 conversion |
TOPK |
10:1 | Top-K by magnitude |
QSGD |
Variable | Quantized SGD |
API Reference
Coordinator
typedef struct {
uint16_t port;
float learning_rate;
float beta1, beta2;
float epsilon;
int min_workers;
int checkpoint_interval;
char checkpoint_dir[SW_MAX_PATH];
sw_compress_type compression;
float topk_ratio;
} sw_coordinator_config;
sw_coordinator* sw_coordinator_new(tm_tensor** weights, int num_weights,
sw_coordinator_config* config);
void sw_coordinator_free(sw_coordinator* coord);
bool sw_coordinator_start(sw_coordinator* coord);
void sw_coordinator_stop(sw_coordinator* coord);
void sw_coordinator_wait(sw_coordinator* coord);
int sw_coordinator_num_workers(sw_coordinator* coord);
int sw_coordinator_get_step(sw_coordinator* coord);
float sw_coordinator_get_loss(sw_coordinator* coord);
Worker
typedef struct {
char coordinator_host[256];
uint16_t coordinator_port;
int reconnect_attempts;
int reconnect_delay_sec;
int num_threads;
int batch_size;
char data_path[SW_MAX_PATH];
int shard_index;
sw_compress_type compression;
float topk_ratio;
char worker_id[SW_MAX_WORKER_ID];
} sw_worker_config;
sw_worker* sw_worker_new(sw_worker_config* config);
void sw_worker_free(sw_worker* w);
bool sw_worker_connect(sw_worker* w);
void sw_worker_disconnect(sw_worker* w);
void sw_worker_set_weights(sw_worker* w, tm_tensor** weights, int num_weights);
void sw_worker_set_forward(sw_worker* w, sw_forward_fn fn, void* ctx);
void sw_worker_set_backward(sw_worker* w, sw_backward_fn fn, void* ctx);
void sw_worker_set_data_loader(sw_worker* w, sw_next_batch_fn next,
sw_reset_data_fn reset, sw_free_batch_fn free_batch,
void* ctx);
bool sw_worker_fetch_weights(sw_worker* w);
void sw_worker_train(sw_worker* w); // Blocking
void sw_worker_stop(sw_worker* w);
Training Callbacks
// Forward: compute loss from batch
typedef float (*sw_forward_fn)(tm_tensor** weights, int num_weights,
void* batch_data, void* ctx);
// Backward: compute gradients
typedef void (*sw_backward_fn)(tm_tensor** weights, int num_weights,
void* batch_data, void* ctx);
// Data loading
typedef void* (*sw_next_batch_fn)(void* ctx);
typedef void (*sw_reset_data_fn)(void* ctx);
typedef void (*sw_free_batch_fn)(void* batch, void* ctx);
Configuration Files
coordinator.toml
[network]
port = 7100
[training]
learning_rate = 1e-4
beta1 = 0.9
beta2 = 0.999
min_workers = 2
[checkpointing]
interval = 1000
directory = "/checkpoints"
[compression]
type = "topk"
topk_ratio = 0.1
worker.toml
[network]
coordinator_host = "coordinator"
coordinator_port = 7100
reconnect_attempts = 10
reconnect_delay_sec = 5
[hardware]
num_threads = 0 # auto
max_memory_mb = 0 # auto
[training]
batch_size = 0 # auto from profile
accumulation_steps = 0 # auto
[data]
path = "/data/train.jsonl"
shard_index = -1 # auto-assign
[compression]
type = "topk"
topk_ratio = 0.1
Hardware Profiles
Auto-detected profiles with recommended batch sizes:
| Profile | Memory | Batch Size | Description |
|---|---|---|---|
RPI3 |
<1GB | 2 | Raspberry Pi 3 |
RPI4 |
1-4GB | 4-8 | Raspberry Pi 4 |
RPI5 |
4-8GB | 8-16 | Raspberry Pi 5 |
LAPTOP |
8-16GB | 16-32 | Standard laptop |
DESKTOP |
16-32GB | 32-64 | Desktop PC |
WORKSTATION |
32-64GB | 64-128 | Workstation |
SERVER |
>64GB | 128-256 | Server |
Docker Deployment
See swarm/docker/ for:
Dockerfile.coordinatorDockerfile.workerdocker-compose.yml(local testing)docker-stack.yml(Swarm deployment)deploy.sh(deployment script)
See Also
- distributed - Local distributed primitives
- tensor - Tensor operations
- optim - Optimizers