Vertex AI CustomJob engine¶
vertex_engine
¶
Vertex AI CustomJob & GCE VM engine — single-VM default + worker-pool sharding + sizing.
Executes any combination of model families on a dedicated Vertex AI CustomJob VM, across a
multi-worker worker_pool_specs pool (or GKE Indexed Job), or on a single Compute Engine VM
(runtime="gce") without the head-node overhead of a Ray cluster or the JVM shuffle overhead of
Spark:
-
Dedicated Per-Model VMs for Global & Deep-Learning Models: Global/hybrid panel models (
tide,tft,tsmixer,patchtst, andneuralprophetwithglobal_fit=True) fit across the entire series panel simultaneously. When multiple global ordeep_learningmodels run in one job,effective_worker_countautomatically allocates 1 dedicated VM per model (even whenworkersis left at its default1), andpartition_models_for_workerassigns each model to its own replica (idx % world_size == rank) on the full series panel. -
Shared ThreadPoolExecutor + Multi-VM Sharding for Local (
statistical/ml) Models: Local per-series models share each VM via a profile-boundedThreadPoolExecutor(slots_per_unitfromplan_vertex_pool, enforcingmin(device, cores, memory)with native thread-pool pinning viaintraop_env_varsand Longest Processing Time first (LPT) ordering). Whenworkers > 1: - If
hierarchy.enabledis False, uniquets_idseries are sorted and partitioned into contiguous ranges per worker (shard_series_for_worker) pushed directly into the BigQuery Storage Read APIrow_restriction. -
If
hierarchy.enabledis True, models are modulo-partitioned across VMs (partition_models_for_worker) so each VM holds the full series tree required to reconcile its assigned model(s) via FPP3reconcile_panel_results. -
Hardware Sizing & Telemetry (
profiling/+resources/):plan_vertex_pooltranslatesComputeProfilemeasurements (or static defaults) into aRuntimeResourcePlanover the VM'sUnitShape, stamped at submit time ($.sizing.<family>) and after the on-VM measurement pre-pass ($.sizing_executed.<family>).
Public surface: WorkerTopology, resolve_worker_topology, effective_worker_count,
models_sharded_across_workers, partition_models_for_worker, shard_series_for_worker,
vertex_unit_shape, plan_vertex_pool, execute_panel, run.
WorkerTopology
dataclass
¶
build_worker_series_range
¶
Build the BigQuery Storage Read API row_restriction for topology.rank (pure).
Returns (row_restriction, worker_ids_set, n_series_total):
- Because limited = sorted_ids[:limit] is a contiguous prefix of all distinct ts_ids in
the table and worker_list = worker_series_slice(limited, topology) is a contiguous
sub-slice of limited, there is no ts_id in the source table between worker_list[0]
and worker_list[-1] outside worker_list. Thus ts_id >= '{start}' AND ts_id <=
'{end}' pushes both series_limit and the worker's 1/W shard into a single server-side
Storage Read API filter.
- When fleetwide HPO is active (hpo_sample_n > 0) on a non-zero rank, OR ts_id <=
'{hpo_bound}' is included so every worker tunes on the identical first-K series sample
before filtering to worker_ids_set.
Source code in src/scale_forecasting/engines/vertex_engine.py
effective_worker_count
¶
Resolve the effective VM replica count so each worker has non-empty work (pure).
- Dedicated per-model VMs for global / deep-learning sets: when
modelscontains $K > 1$ global ordeep_learningmodels (_is_dedicated_vm_model_set), a defaultrequested_workers == 1automatically scales tolen(models)so each model gets its own dedicated VM; an explicitrequested_workers > 1is capped atlen(models)when all models are panel models (ormodels_sharded_across_workersis True). - Hierarchical reconciliation (
cfg.hierarchy.enabled): models are partitioned across VMs while keeping the full hierarchy tree on each VM, so up tomin(requested_workers, len(models))VMs can run in parallel. - Local per-series models (
statistical,ml, or single per-seriesneuralprophet): series are sharded across VMs, bounded byn_serieswhen known.
Source code in src/scale_forecasting/engines/vertex_engine.py
execute_panel
¶
Pure, in-memory execution of one worker's slice of source (no GCP I/O).
Partitions models and/or shards series according to topology and delegates to the shared
spark_io.run_group kernel so local, global/hybrid, and hierarchical reconciliation behave
identically to Spark and Ray.
Source code in src/scale_forecasting/engines/vertex_engine.py
models_sharded_across_workers
¶
True when multi-VM workers partition models by model index (each on the full panel).
Happens in three cases:
1. cfg.hierarchy.enabled is True — reconcile_panel_results reconciles each model_type
independently across the entire series tree, so different VMs can run different models on the
full panel in parallel.
2. Every model in models is a panel (global/hybrid) model.
3. Multiple deep_learning / panel models run in the same job (len(models) > 1 and
_is_dedicated_vm_model_set), giving each deep-learning model its own dedicated VM on the
full panel.
Source code in src/scale_forecasting/engines/vertex_engine.py
order_chunks_lpt
¶
Order intra-VM chunks Longest-Processing-Time-First (LPT) to minimize tail latency (pure).
Scores each chunk by its row count weighted by _MODEL_LPT_WEIGHTS so heavy models (sarimax,
auto_arima, prophet, xgboost) and longer series histories start first in
ThreadPoolExecutor, while fast chunks (theta, naive_mean) backfill around them.
Source code in src/scale_forecasting/engines/vertex_engine.py
partition_models_for_worker
¶
Deterministically assign the model subset that topology.rank should execute (pure).
- Single worker (
world_size == 1): executes allmodels. - Model-sharded jobs (
models_sharded_across_workers— hierarchy enabled, all panel models, or multiple deep-learning models): modulo-assignsmodelsacross workers (idx % world_size == rank), with each worker running its assigned model(s) on the full series panel. - Mixed / local jobs: any panel models in
modelsreceive modulo assignment (panel_idx % world_size == rank), while local per-series models are kept on all active workers and sharded byts_idviashard_series_for_worker.
Source code in src/scale_forecasting/engines/vertex_engine.py
plan_vertex_pool
¶
plan_vertex_pool(
cfg,
models,
n_cells,
*,
runtime="vertex",
gpu=False,
gpu_type=None,
machine_type=None,
worker_count=1,
profile=None,
)
Size the per-VM slot density and worker fleet for a Vertex CustomJob or GCE VM (pure).
Uses the shared resources.slot.resource_slot + resources.fleet.plan_fleet algebra:
- Each cell's ResourceSlot is derived from profile (or static defaults when unprofiled).
- Because "vertex" and "gce" are not in _UNENFORCED_AXES, plan_fleet enforces all three
axes (min(device, cores, memory)) against schedulable_cores(unit) and
schedulable_memory_bytes(unit).
- plan.slots_per_unit bounds ThreadPoolExecutor(max_workers=...) on each VM, and
intraop_env_vars(plan.slot.cores, include_omp=True) pins native BLAS/OMP threads per slot.
Source code in src/scale_forecasting/engines/vertex_engine.py
resolve_worker_topology
¶
Resolve (rank, world_size) from args, env vars, or Vertex AI CLUSTER_SPEC (pure).
Precedence:
1. Explicit worker_rank / worker_count arguments (--worker-rank / --worker-count).
2. Explicit environment variables SF_WORKER_RANK / SF_WORKER_COUNT.
3. Kubernetes Indexed Job environment variables JOB_COMPLETION_INDEX /
JOB_COMPLETION_TOTAL (or JOB_COMPLETION_COUNT / SF_WORKER_COUNT).
4. Vertex AI CLUSTER_SPEC JSON ({"cluster": {"workerpool0": [...], "workerpool1": [...]},
"task": {"type": "workerpool1", "index": 0}}).
5. Single-machine fallback WorkerTopology(rank=0, world_size=1, source="single").
Source code in src/scale_forecasting/engines/vertex_engine.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | |
run
¶
Execute a Vertex AI CustomJob, GCE VM, or GKE Indexed Job worker end-to-end.
Structural twin of ray_engine.run and spark_explode.run, invoked by vertex_entry.
Source code in src/scale_forecasting/engines/vertex_engine.py
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 | |
shard_series_for_worker
¶
Deterministically shard a sequence of series IDs or panel into a contiguous slice (pure).
Source code in src/scale_forecasting/engines/vertex_engine.py
vertex_unit_shape
¶
One VM worker of a Vertex CustomJob or GCE instance: cores, RAM, and accelerators (pure).
Source code in src/scale_forecasting/engines/vertex_engine.py
worker_series_slice
¶
Return the contiguous slice of sorted_ids assigned to topology.rank (pure).
Partitions n = len(sorted_ids) into world_size contiguous, non-overlapping slices
[(n * rank) // world_size : (n * (rank + 1)) // world_size]. Contiguous slicing enables
pushing ts_id >= '{start_id}' AND ts_id <= '{end_id}' directly into the BigQuery Storage
Read API row_restriction so each VM in a multi-VM worker pool streams only its own 1/W
slice of the table over Arrow gRPC.