bland-waiter-14993
03/17/2024, 8:55 PMstart and then use foreach for every batch. However, how do I then ensure that the information extraction step works for only 1 batch at a time? Do I use internal semaphore?elegant-lamp-40213
03/17/2024, 9:52 PMforeach together with safeguards like --max-workers (see https://docs.metaflow.org/scaling/remote-tasks/introduction#safeguard-flags).
This would provide you some flexibility in case you intend to scale-out in the future. Although you'd have to evaluate the potential overhead of running each batch one at a time in a separate worker, as compared with sequentially processing the batches one at a time from the same worker.bland-waiter-14993
03/17/2024, 10:00 PM--max-workers=1 but it seems by default Metaflow wants to end one step completely before proceeding with the next. Do you think there is any way to enforce task execution like this: processing 0 -> saving 0 -> processing 1 -> saving 1 -> ...
I do understand worst case I could merge the two steps into onefresh-laptop-72652
03/18/2024, 6:13 AMjoin step requires all the previous parallel steps to have finished. In real-world use cases on Step Functions or Argo, those orchestrators will schedule the save after the process to be executed. If you think of a basic first-in-first-out job queue, you can see how this can result in your screenshot. There's of course a ton of ways those job queues/orchestrators can be configured, and it also depends on the compute requirements of the steps (e.g. some steps are CPU only, others might require GPUs) and if each of those tasks can be executed immediately or if scaling up new nodes is required before placement, etc etc
as for how to do the batching itself, that really depends on the specifics of your problem – e.g. how large the documents are will likely inform how many you process at once, if a single "failed" batch should cause the whole "set of batches" to fail (if a step fails, the whole flow will fail), and so onbland-waiter-14993
03/18/2024, 8:22 AMjoin step is added because Metaflow flow validator complains if there is no join step before end. I'm not allowed to remove this, even though I don't need it.
The reason I want execution like this:
processing 0 -> saving 0 -> processing 1 -> saving 1 -> ...
is because of memory constraints. Processing each batch of documents will produce a result object and it's just a risk for out of memory if I'm processing >200k documents with a pipeline. So I want to finish one batch and begin other batch.
In my case, I only have 1 GPU machine (which will execute other short steps too) and it can only process 1000 documents at once (1 batch).
I was starting my test like this:
python preprocessing/tests/test_metaflow_ratelimit.py run --max-workers=1bland-waiter-14993
03/18/2024, 8:25 AMelegant-lamp-40213
03/18/2024, 8:32 AMbland-waiter-14993
03/18/2024, 8:40 AMelegant-lamp-40213
03/18/2024, 9:39 AMelegant-lamp-40213
03/18/2024, 9:44 AMbland-waiter-14993
03/18/2024, 11:55 AMelegant-lamp-40213
03/18/2024, 12:19 PMfresh-laptop-72652
03/18/2024, 3:08 PMThe reason I want execution like this:
processing 0 -> saving 0 -> processing 1 -> saving 1 -> ...
is because of memory constraints. Processing each batch of documents will produce a result object and it's just a risk for out of memory if I'm processing >200k documents with a pipeline. So I want to finish one batch and begin other batch.
In my case, I only have 1 GPU machine (which will execute other short steps too) and it can only process 1000 documents at once (1 batch).you can control the CPU/RAM/GPU resources allocated at the step level, so that's easy to line up with your desired batching strategy you can configure your backend compute environments so nodes with GPUs only process steps that request GPUs (e.g. create separate Batch job queues if deploying Step Functions, or similar taints/tolerations/etc in the k8s/argo stack) -- that's what I meant by there being some scheduler considerations that might vary depending how you've configured your deployment you can absolutely have a single GPU machine that will execute the
process tasks, and then CPU machines that will handle the save tasks as they become available. The subsequent save tasks (within each of the parallel branches) will not wait until all process tasks have finished. The reason you're seeing the execution ordering in the screenshot is not due to the DAG/parallel fanout, but is due to how the toy example is constructed
whether or not you want to have a single flow, with a single step that iterates through the batches to rate limit, or a single flow with a parallel foreach fanout to process in parallel, or separate flow executions per batch really just depends on what you want and the specifics of your problem.
the main considerations that come to my mind for that are that changing the number of parallel steps or flow executions doesn't change the specifics of your compute environment – if you only have a single GPU node, you aren't addressing bottlenecks through those changes in the DAG structure. Similarly if the API you're hitting has tight limits, then it may not make sense to try to increase throughput with parallel requests on your end – really just depends how/why those limits are in place to begin with.
The larger question I think is around what you are considering a "unit of work" in the case of writes to the DB and when addressing failures -- e.g. if a run fails and you need to retry it, does it need to retry only a single batch or all the batches. Answering that question can help inform how you structure the flow and what makes sense to persist as each step's artifactsbland-waiter-14993
03/18/2024, 3:29 PMfresh-laptop-72652
03/18/2024, 3:54 PMforeach split the process/save happen in the correct order, that is guaranteed to happen by the structure of the DAG
at the "workflow" level, the steps will be executed as they become available (and depending on some job orchestrator specifics)
as an example of having some of the save happen in specific branches of the parallel fanout, while process are still going on in other branches of the fanout, give it a go yourself!
check out the outerbounds sandbox to take it for a spin
https://outerbounds.com/sandbox/
I copied your code into a test.py
then I deployed the flow to a proper orchestrator (Argo in this case) via
python test.py argo-workflows create
and then triggered an execution via
python test.py argo-workflows trigger
and in the UI you can see the run, along with the ordering of each of those steps