Skip to content

Commit 2f7d95d

Browse files
[Gemini] Add batching support to Java Remote Inference (#39067)
* [Gemini] Add batching support to Java Remote Inference * Update sdks/java/ml/inference/remote/src/test/java/org/apache/beam/sdk/ml/inference/remote/RemoteInferenceTest.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Fix batching test issue --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 8c0f0e0 commit 2f7d95d

2 files changed

Lines changed: 49 additions & 24 deletions

File tree

sdks/java/ml/inference/remote/src/main/java/org/apache/beam/sdk/ml/inference/remote/RemoteInference.java

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@
2020
import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.Preconditions.checkArgument;
2121

2222
import com.google.auto.value.AutoValue;
23-
import java.util.Collections;
2423
import java.util.List;
24+
import org.apache.beam.sdk.transforms.BatchElements;
2525
import org.apache.beam.sdk.transforms.DoFn;
26-
import org.apache.beam.sdk.transforms.MapElements;
2726
import org.apache.beam.sdk.transforms.PTransform;
2827
import org.apache.beam.sdk.transforms.ParDo;
29-
import org.apache.beam.sdk.transforms.SimpleFunction;
3028
import org.apache.beam.sdk.values.PCollection;
3129
import org.checkerframework.checker.nullness.qual.Nullable;
3230

@@ -82,6 +80,8 @@ public abstract static class Invoke<InputT extends BaseInput, OutputT extends Ba
8280

8381
abstract @Nullable BaseModelParameters parameters();
8482

83+
abstract BatchElements.@Nullable BatchConfig batchConfig();
84+
8585
abstract Builder<InputT, OutputT> builder();
8686

8787
@AutoValue.Builder
@@ -92,6 +92,8 @@ abstract Builder<InputT, OutputT> setHandler(
9292

9393
abstract Builder<InputT, OutputT> setParameters(BaseModelParameters modelParameters);
9494

95+
abstract Builder<InputT, OutputT> setBatchConfig(BatchElements.BatchConfig batchConfig);
96+
9597
abstract Invoke<InputT, OutputT> build();
9698
}
9799

@@ -106,21 +108,26 @@ public Invoke<InputT, OutputT> withParameters(BaseModelParameters modelParameter
106108
return builder().setParameters(modelParameters).build();
107109
}
108110

111+
/** Configures the batching behavior for the inputs. */
112+
public Invoke<InputT, OutputT> withBatchConfig(BatchElements.BatchConfig batchConfig) {
113+
return builder().setBatchConfig(batchConfig).build();
114+
}
115+
109116
@Override
110117
public PCollection<Iterable<PredictionResult<InputT, OutputT>>> expand(
111118
PCollection<InputT> input) {
112119
checkArgument(handler() != null, "handler() is required");
113120
checkArgument(parameters() != null, "withParameters() is required");
114-
return input
115-
.apply(
116-
"WrapInputInList",
117-
MapElements.via(
118-
new SimpleFunction<InputT, List<InputT>>() {
119-
@Override
120-
public List<InputT> apply(InputT element) {
121-
return Collections.singletonList(element);
122-
}
123-
}))
121+
122+
BatchElements.BatchConfig config = batchConfig();
123+
PCollection<List<InputT>> batchedInput;
124+
if (config != null) {
125+
batchedInput = input.apply("BatchElements", BatchElements.withConfig(config));
126+
} else {
127+
batchedInput = input.apply("BatchElements", BatchElements.withDefaults());
128+
}
129+
130+
return batchedInput
124131
// Pass the list to the inference function
125132
.apply("RemoteInference", ParDo.of(new RemoteInferenceFn<InputT, OutputT>(this)));
126133
}

sdks/java/ml/inference/remote/src/test/java/org/apache/beam/sdk/ml/inference/remote/RemoteInferenceTest.java

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -518,36 +518,54 @@ public void testPredictionResultMapping() {
518518
pipeline.run().waitUntilFinish();
519519
}
520520

521-
// Temporary behaviour until we introduce java BatchElements transform
522-
// to batch elements in RemoteInference
523-
@Test
524-
public void testMultipleInputsProduceSeparateBatches() {
525-
List<TestInput> inputs = Arrays.asList(new TestInput("input1"), new TestInput("input2"));
521+
private static class GenerateInputsFn
522+
extends org.apache.beam.sdk.transforms.DoFn<Integer, TestInput> {
523+
@ProcessElement
524+
public void processElement(ProcessContext c) {
525+
c.output(new TestInput("input1"));
526+
c.output(new TestInput("input2"));
527+
}
528+
}
526529

530+
@Test
531+
public void testBatchingProducesCombinedBatches() {
527532
TestParameters params = TestParameters.builder().setConfig("test-config").build();
528533

534+
// Use a single element to trigger generation of inputs within the same bundle,
535+
// ensuring DirectRunner doesn't split them before BatchElements processes them.
529536
PCollection<TestInput> inputCollection =
530-
pipeline.apply(
531-
"CreateInputs", Create.of(inputs).withCoder(SerializableCoder.of(TestInput.class)));
537+
pipeline
538+
.apply("CreateTrigger", Create.of(1))
539+
.apply(
540+
"GenerateInputs", org.apache.beam.sdk.transforms.ParDo.of(new GenerateInputsFn()))
541+
.setCoder(SerializableCoder.of(TestInput.class));
542+
543+
// Configure BatchElements to force a batch of exactly 2
544+
org.apache.beam.sdk.transforms.BatchElements.BatchConfig batchConfig =
545+
org.apache.beam.sdk.transforms.BatchElements.BatchConfig.builder()
546+
.withMinBatchSize(2)
547+
.withMaxBatchSize(2)
548+
.build();
532549

533550
PCollection<Iterable<PredictionResult<TestInput, TestOutput>>> results =
534551
inputCollection.apply(
535552
"RemoteInference",
536553
RemoteInference.<TestInput, TestOutput>invoke()
537554
.handler(MockSuccessHandler.class)
555+
.withBatchConfig(batchConfig)
538556
.withParameters(params));
539557

540558
PAssert.that(results)
541559
.satisfies(
542560
batches -> {
543561
int batchCount = 0;
562+
int totalElements = 0;
544563
for (Iterable<PredictionResult<TestInput, TestOutput>> batch : batches) {
545564
batchCount++;
546-
int elementCount = (int) StreamSupport.stream(batch.spliterator(), false).count();
547-
// Each batch should contain exactly 1 element
548-
assertEquals("Each batch should contain 1 element", 1, elementCount);
565+
totalElements += (int) StreamSupport.stream(batch.spliterator(), false).count();
549566
}
550-
assertEquals("Expected 2 batches", 2, batchCount);
567+
assertEquals("Expected 1 batch", 1, batchCount);
568+
assertEquals("Total output elements should be 2", 2, totalElements);
551569
return null;
552570
});
553571

0 commit comments

Comments
 (0)