Ting Chen
04/07/2025, 1:36 AMBentoML supports adaptive batching, a dynamic request dispatching mechanism that intelligently groups multiple requests for more efficient processing. It continuously adjusts batch size and window based on real-time traffic patterns.
The reason I ask is because I have set up some load test, and logged out the real batch size received from the inference function, but I don't see any dynamic size coming in. So I am wondering any setup was not correct or I misunderstand this feature. Can anyone help me with this? 🙏Jian Shen Yap
04/07/2025, 3:54 AMTing Chen
04/07/2025, 3:57 AM@bentoml.service(workers=int(number_of_workers))
class RealModel:
def __init__(self):
self.custom_inference = load_custom_inference_module()
self.model_dir = os.environ['ZD_INFERENCE_MODEL_DIR']
self.loaded_model = self.custom_inference.model_fn(self.model_dir)
if hasattr(self.custom_inference, 'warm_up'):
self.custom_inference.warm_up(self.loaded_model)
else:
bentoml_logger.warning("The custom_inference module does not have a warm_up function.")
@bentoml.api
def model_loaded(self) -> bool:
return self.loaded_model is not None
@bentoml.api
def input_array_fn(self, input_str: str, content_type: str) -> List[Any]:
result = self.custom_inference.input_array_fn(input_str, content_type)
return result
@bentoml.api(batchable=True, max_batch_size=max_batch_size, max_latency_ms=max_latency_ms)
def predict_array_fn(self, input_tensors: List[Any]) -> List[Any]:
bentoml_logger.info(f"Batch size: {len(input_tensors)}")
return self.custom_inference.predict_array_fn(input_tensors, self.loaded_model)
@bentoml.api
def output_array_fn(self, predictions: List[Any], content_type: str) -> Any:
return self.custom_inference.output_array_fn(predictions, content_type)
// other functions
@bentoml.service(
workers=int(number_of_workers),
traffic={"timeout": 60},
logging={
"access": {
"enabled": True,
"request_content_length": True,
"request_content_type": True,
"response_content_length": True,
"response_content_type": True,
"skip_paths": ["/metrics", "/healthz", "/livez", "/readyz"],
"format": {
"trace_id": "032x",
"span_id": "016x"
}
}
}
)
@bentoml.mount_asgi_app(app)
class ServeModel:
name = "Zendesk Model Serving"
model = bentoml.depends(RealModel)
@bentoml.api(route='predictions/model')
def invoke(
self,
inputs: Dict,
ctx: bentoml.Context
) -> Dict:
content_type = ctx.request.headers.get("content-type")
input_str = json.dumps(inputs)
if self.adaptive_batch_enabled:
input_tensors = self.model.input_array_fn(input_str, content_type)
predictions = self.model.predict_array_fn(input_tensors)
predict_response = self.model.output_array_fn(predictions, 'application/json')
else:
input_tensors = self.model.input_fn(input_str, content_type)
predictions = self.model.predict_fn(input_tensors)
predict_response = self.model.output_fn(predictions, 'application/json')
if isinstance(predict_response, str):
dict_response = json.loads(predict_response)
else:
dict_response = predict_response
if not isinstance(dict_response, Dict):
raise ValueError("Output from custom inference is not a valid dictionary")
return dict_responseTing Chen
04/07/2025, 4:00 AMdef predict_array_fn(
input_data: Union[Dict, List[Dict]], model: EmbeddingModelE5MultilingualLargeOnnx
) -> List[List[float]]:
"""Performs embeddings on the input text, using the loaded model."""
input_datas = [input_data] if not isinstance(input_data, list) else input_data
print(f"Real Batch size: {len(input_datas)}")
embeddings = model.get_embeddings(input_datas)
return embeddings
def input_array_fn(request_body: str, request_content_type: str) -> List[Dict]:
"""Loads text content from request."""
if request_content_type.startswith("application/json"):
input_data = json.loads(request_body)
if "prefix_type" in input_data:
input_prefix_type = input_data["prefix_type"] if not isinstance(input_data["prefix_type"], list) \
else input_data["prefix_type"][0]
try:
prefix_type = EmbeddingPrefixEnum[input_prefix_type]
except KeyError:
raise ValueError(f"'{input_prefix_type}' is not a valid name in {EmbeddingPrefixEnum.__name__}")
else:
prefix_type = EmbeddingPrefixEnum.passage
return [{"text": text, "prefix_type": prefix_type.value} for text in input_data["texts"]]
else:
raise ValueError(
f"request_content_type {request_content_type} not supported. Must be 'application/json'"
)
def output_array_fn(predictions: List[List[float]], content_type: str) -> Dict[str, List[List]]:
"""Serializes the prediction result into a JSON formatted list of embeddings."""
if content_type != "application/json":
raise ValueError(
f"content_type {content_type} not supported. Must be 'application/json'"
)
return {"embeddings": predictions}Ting Chen
04/07/2025, 4:01 AMJian Shen Yap
04/07/2025, 6:46 PMmax_batch_size=max_batch_size
max_latency_ms=max_latency_ms
parameter.
can you share what is the value of these parameter, as well as the average latency for 1 prediction?
also, are you firing alot of request at once and expect it to batch, but it doesn't?Jian Shen Yap
04/07/2025, 6:47 PMbentoml_service_adaptive_batch_size_* related metrics when you check at /metrics endpoint, it wil give you the insights about the adaptive batchingTing Chen
04/08/2025, 12:05 AMmax_batch_size=5
max_latency_ms=200000
average latency for 1 prediction would be less than 100ms
I am firing a lot of requests at once and keep adding more requests slowly and expect it to batch, and so far checking the batch size send to the group function is still same as what I have in the original requests.
I haven't check the metrics so far, I will check it later. thanks!Ting Chen
04/08/2025, 11:49 PM# HELP bentoml_service_adaptive_batch_size Service adaptive batch size
# TYPE bentoml_service_adaptive_batch_size histogram
bentoml_service_adaptive_batch_size_sum{method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="2"} 25015.0
bentoml_service_adaptive_batch_size_sum{method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="1"} 25506.0
bentoml_service_adaptive_batch_size_bucket{le="1.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="2"} 25015.0
bentoml_service_adaptive_batch_size_bucket{le="2.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="2"} 25015.0
bentoml_service_adaptive_batch_size_bucket{le="4.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="2"} 25015.0
bentoml_service_adaptive_batch_size_bucket{le="5.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="2"} 25015.0
bentoml_service_adaptive_batch_size_bucket{le="+Inf",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="2"} 25015.0
bentoml_service_adaptive_batch_size_count{method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="2"} 25015.0
bentoml_service_adaptive_batch_size_bucket{le="1.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="1"} 25506.0
bentoml_service_adaptive_batch_size_bucket{le="2.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="1"} 25506.0
bentoml_service_adaptive_batch_size_bucket{le="4.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="1"} 25506.0
bentoml_service_adaptive_batch_size_bucket{le="5.0",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="1"} 25506.0
bentoml_service_adaptive_batch_size_bucket{le="+Inf",method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="1"} 25506.0
bentoml_service_adaptive_batch_size_count{method_name="predict_array_fn",runner_name="RealModel",service_name="RealModel",service_version="not available",worker_index="1"} 25506.0Jian Shen Yap
04/09/2025, 10:07 AM