Sean Sall
03/06/2024, 8:32 PMSean Sall
03/06/2024, 8:34 PMmethod_configs={"infer": {"max_batch_size": 2, "max_latency_ms": 100}}
The server is able to handle ~1/3 the traffic as if I just hadn't implemented batching in the first place. And it's not because it's actually inferring on batches and the inference requests to the runners are taking longer. I've confirmed that there are only single images getting passed to the runners (requests are not coming in fast enough for batches to be larger than a single image).Sean Sall
03/06/2024, 8:35 PMSean Sall
03/06/2024, 8:36 PMJian Shen Yap
03/06/2024, 9:40 PM{"max_batch_size": 2, "max_latency_ms": 100}
The error you've seen usually means the scheduler is rejecting additional request because its unable to satisfy the latency requirement.Jian Shen Yap
03/06/2024, 9:41 PMSean Sall
03/06/2024, 9:41 PMJian Shen Yap
03/06/2024, 9:42 PMbatch size 1 = no batching on the implementation leveland we will review this! thanks for spotting
Sean Sall
03/06/2024, 9:43 PMmax_latency_ms parameter? I've got a runner with latency around ~1s, and I'm seeing that with load testing it's best to have max_latency_ms around 32000 (32 seconds) for batching to maximize throughput. But, in production we can't actually afford to have requests take an extra 32 seconds, so I'm wary of actually using that 32000 value.Jian Shen Yap
03/06/2024, 9:46 PMmax_latency_ms accordingly to the SLA of your service.
in practice to maximize your throughput and latency, it will be a combination of horizontal + vertical scaling,+ adaptive batching which is what bentoml offers.
if you cannot afford to have a high latency, i would suggest having an acceptable max_latency_ms and scale your services horizontally, either using more workers / have multiple replicasSean Sall
03/06/2024, 9:54 PMmax_latency_ms? Would it be some multiple of the latency of the runner?Sean Sall
03/06/2024, 9:55 PMSean Sall
03/06/2024, 11:40 PMbento service when we hit it? I keep getting 503 errors that tell me the server is busy, but I've got multiple runners in the service and I'm not sure how to track down which one is the long pull here. Are there tools built into Bento to try to sort this out?Sean Sall
03/06/2024, 11:42 PMThe error you've seen usually means the scheduler is rejecting additional request because its unable to satisfy the latency requirementCan you give me more detail on what calculations the scheduler is making on the backend to reject the additional requests? It's unclear to me how to tune the combo of
max_batch_size and max_latency_ms here, and I think understanding that would help.Jian Shen Yap
03/07/2024, 3:06 PMrecommended optimal setting forThere's no single answer for this. "Optimal" depends on what are you trying to optimize for. You will have to play around with different combinations of settings and see what fits you the best!max_latency_ms
Sean Sall
03/07/2024, 3:08 PMJian Shen Yap
03/07/2024, 3:57 PMreject the additional requestsit will predict if it can satisfy
max_latency_ms , if not, it will reject the request.
latency of the runner and multiply it by 2i think you can start by multiple of 2/3. But this is also depends on how batching is implemented for your model. We can assume most model implement batching correctly, then the above setting is a good starting point. It also depends on the resource utilization, having batching means it will take up more compute resource on your CPU/GPU.
Jian Shen Yap
03/07/2024, 3:57 PMI'm not seeing anything that precise in the docs for batching,Yeah i guess we should be more clear on it. I'll feedback into the team!
Sean Sall
03/07/2024, 4:01 PMit will predict if it can satisfyGotcha, this is very helpful to know. When I'm performing load testing with batching, it seems that I'm getting 503 errors (server busy) more quickly than without batching. I'm suspecting that it's because of the reject mechanism here, where with batching, the requests get rejected because of this, if not, it will reject the requestmax_latency_ms
predict if can satisfy max_latency_ms condition, whereas without batching they would just timeout according to the runner timeout. Is that right? Or how does the non-batching case work in terms of rejecting requests to a runner?
But this is also depends on how batching is implemented for your model. We can assume most model implement batching correctly, then the above setting is a good starting point.Out of curiosity, what are the wrong / right ways to have batching implemented? We're not seeing as large of speed gains from batching as we had thought, so perhaps we do have it implemented incorrectly.
Jian Shen Yap
03/07/2024, 4:40 PMmore quickly than without batching. I'm suspecting that it's because of the reject mechanism here, where with batching, the requests get rejected because of thisYou are right! if you relax thecondition, whereas without batching they would just timeout according to the runner timeout. Is that right? Or how does the non-batching case work in terms of rejecting requests to a runner?predict if can satisfy max_latency_ms
max_latency_ms you should see that less.
Generally theres two scenario that you will see this error:
1. the http Server is overloaded and is not responsive.
2. the scheduler rejects the request ( only if batching it turned on )Jian Shen Yap
03/07/2024, 4:43 PMOut of curiosity, what are the wrong / right ways to have batching implemented?one example of a wrong way if implementing batching is:
def batch_predict(input: list[str]):
results = [predict(i) for i in input]
return results
One way to measure how batching is properly implemented, is predict 10 request at once takes almost the same time as predicting 1 request. Usually most DL model is built to support batch predictSean Sall
03/07/2024, 4:53 PMYou are right! if you relax theOkay rad, I think I'm starting to grok this. One more question to check my understanding. In effect, when we turn on batching, theyou should see that less.max_latency_ms
max_latency_ms is acting as the runner timeout value, yeah?
Gotcha on the batching! We're not implementing batching in that way 😅 .
One way to measure how batching is properly implemented, is predict 10 request at once takes almost the same time as predicting 1 request. Usually most DL model is built to support batch predict.Out of curiosity, and this is now down the DL rabbit hole a bit, but do you have any references that show inference time speedups with batching? I haven't actually been able to find much via google - most references I've seen mention training time. Here's a chart of the speedup we've been seeing across different models (model names blurred for privacy), and it's not as high as I would have thought, and nowhere near the "predicting on X requests is nearly the same as 1 request" (which is roughly what we thought we'd see when doing batching). And the pseudocode way we've been doing batching is basically:
dl = PyTorchModel()
input = TorchTensor()
for batch_size in [1, 2, 4, 8, 16, 32]:
batched_input = torch.stack([input] * batch_size)
dl(batched_input)Jian Shen Yap
03/07/2024, 4:56 PMbentoml serve --debug there you can see some of the "Scheduler" logs and the dynamic batch sizesSean Sall
03/07/2024, 4:56 PMalso you can turn onAhhh, amazing thank you!there you can see some of the "Scheduler" logs and the dynamic batch sizesbentoml serve --debug
Sean Sall
03/07/2024, 5:27 PMThe maximum time in milliseconds that a batch will wait to accumulate more requests before processingThis seems to suggest that the server / scheduler will wait
max_latency_ms seconds for a batch to accumulate, whereas as we've talked about here the scheduler more or less uses the max_latency_ms as a runner timeout parameter. The former seems reactive, whereas the latter seams proactive. It sounds like the former (the _will wait max_latency_ms)_ seconds is not actually what happens on the backend - is that right?
What's the default for max_latency_ms? From the testing I'm doing it seems like maybe it's the default service timeout of 60 seconds?Jian Shen Yap
03/14/2024, 8:20 AMmax_latency_ms <= time_waiting_in_batch + request_processing_timeJian Shen Yap
03/14/2024, 8:23 AMmax_batch_size = 100
max_latency_ms = 60000
https://github.com/bentoml/BentoML/blob/main/src/_bentoml_sdk/decorators.py#L66Sean Sall
03/14/2024, 4:41 PMmax_batch_size as say, 4, and the max_latency_ms unspecified (which puts it at the default), and then perform a load test with 1 user (so requests coming in fairly slowly, but still at least 1 per second) - the runner scheduler doesn't wait the full 60000 ms for a batch. It instead appears to converge to a batch size of 1 and just constantly take a batch of 1. It seems that the scheduler is taking the max_batch_size and max_latency_ms as upper bounds, and then using some other algorithm to decide how long it will actually wait for another request. Do y'all have more detail on exactly how that algorithm works?