AI System Scalability Questions
Covers designing and operating machine learning systems to handle growth in data volume, model complexity, and traffic. Topics include distributed training strategies such as data parallelism, model parallelism, and pipeline parallelism; coordination and orchestration approaches like parameter servers, gradient aggregation, and framework tools such as PyTorch distributed, Horovod, and TensorFlow strategies; data pipeline and I O considerations including sharding, efficient formats, preprocessing bottlenecks, streaming and batch ingestion; serving and inference scaling including model sharding, batching for throughput, autoscaling, request routing, caching, and latency versus throughput tradeoffs. Also includes monitoring, profiling, checkpointing and recovery, reproducibility, cost and resource optimization, and common bottleneck analysis across network, storage, CPU preprocessing, and accelerator utilization.
Sample Answer
# Pseudocode for shard save/commit and elastic restore
# Local per-worker save (each worker i):
function WorkerSave(worker_id, epoch, local_params, local_opt_state, storage):
# compute per-tensor slices owned by this worker: list of (tensor_name, slice_range)
slices = compute_owned_slices(worker_id)
local_files = []
for (name, rng) in slices:
data = serialize(local_params[name][rng], local_opt_state[name][rng])
fname = f"{model_name}/epoch{epoch}/shard_w{worker_id}/{name}.{rng}.bin"
checksum = storage.put(fname, data) # upload immutable file
local_files.append({ "name": name, "range": rng, "file": fname, "checksum": checksum, "bytes": len(data)})
# local manifest: what this worker saved
local_manifest = {
"worker_id": worker_id,
"epoch": epoch,
"timestamp": now(),
"files": local_files,
"local_version": compute_version(local_params, local_opt_state)
}
storage.put(f"{model_name}/epoch{epoch}/shard_w{worker_id}/manifest.json", json.dumps(local_manifest))
# optionally notify coordinator (RPC or pubsub)
coordinator.notify_worker_manifest(worker_id, local_manifest)
# Coordinator commit (collect and write global manifest)
function CoordinatorCommit(epoch, expected_worker_count, storage, timeout=30s):
manifests = wait_for_manifests(expected_worker_count, timeout)
# validate no overlapping ownership, full coverage of global tensors
assert validate_coverage(manifests)
global_manifest = {
"model_name": model_name,
"epoch": epoch,
"version": format_version(),
"workers": [m.worker_id for m in manifests],
"shards": concat(m.files for m in manifests),
"global_tensors": list_global_tensor_shapes_and_dtypes(),
"timestamp": now(),
"checksums_ok": all(storage.verify(f.file, f.checksum) for f in shards)
}
# write atomically (write to temp then rename)
tmp = f"{model_name}/epoch{epoch}/global_manifest.json.tmp"
storage.put(tmp, json.dumps(global_manifest))
storage.rename(tmp, f"{model_name}/epoch{epoch}/global_manifest.json")
# publish commit marker
storage.put(f"{model_name}/epoch{epoch}/COMMITTED", b"1")
return global_manifest
# Elastic restore on new cluster with N' workers
function ElasticRestore(target_epoch, new_num_workers, storage):
manifest = storage.get_json(f"{model_name}/epoch{target_epoch}/global_manifest.json")
assert storage.exists(f"{model_name}/epoch{target_epoch}/COMMITTED")
# compute mapping from old shard ranges -> new worker assignments
global_tensors = manifest.global_tensors
shards = manifest.shards
mapping = remap_shards_to_new_workers(shards, new_num_workers, global_tensors)
# two modes: eager (repartition and materialize) or lazy (stream and assemble on-the-fly)
for new_worker_id in range(new_num_workers):
assigned_segments = mapping[new_worker_id] # list of (file, byte_range, target_range)
# fetch segments and reconstruct local params+opt state
local_state = {}
for seg in assigned_segments:
data = storage.get_range(seg.file, seg.byte_range) # range-read if supported
tensor_part = deserialize(data, seg.target_range)
merge_into(local_state, seg.tensor_name, tensor_part)
# optionally write new local shard files for faster future loads
write_local_shard_cache(new_worker_id, target_epoch, local_state)
return success
# Helper: remap_shards_to_new_workers
function remap_shards_to_new_workers(old_shards, new_num):
# For each global tensor, treat as contiguous linear index; compute new ranges by split
mapping = dict(new_worker_id -> [])
for tensor in global_tensors:
total_len = tensor.shape.num_elements()
for new_worker_id in range(new_num):
new_range = compute_range_for_worker(new_worker_id, new_num, total_len)
# find which old shard files overlap and emit (file, overlap_byte_range, target_range)
overlaps = find_overlaps(old_shards_for_tensor(tensor.name), new_range)
mapping[new_worker_id].extend(overlaps)
return mappingSample Answer
Sample Answer
Sample Answer
Sample Answer
Unlock Full Question Bank
Get access to hundreds of AI System Scalability interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.