I was looking at a flame graph of an ingestion workload with @dliappis, and it looks like we parse the JSON representation of the index request twice when a document has both a pipeline and a final pipeline.

It's hard to know how much we would save on this benchmark since not all data streams in this benchmark had both a pipeline and a final pipeline, but the cost of parsing index request into maps of maps via IngestService represented 4.06% of overall CPU time according to the flame graph, so this might not be negligible.
server/src/main/java/org/elasticsearch/ingest/IngestService.java
"failed to execute pipeline [%s] for document [%s/%s]",
                            pipelineId,
                            indexRequest.index(),
                            indexRequest.id()
                        ),
                        e
                    );
                    onFailure.accept(slot, e);
                    
                    onFinished.onResponse(null);
                    return;
                }
                Iterator<String> newIt = it;
                boolean newHasFinalPipeline = hasFinalPipeline;
                String newIndex = indexRequest.indices()[0];
                if (Objects.equals(originalIndex, newIndex) == false) {
                    if (hasFinalPipeline && it.hasNext() == false) {
                        totalMetrics.ingestFailed();
                        onFailure.accept(
                            slot,
                            new IllegalStateException("final pipeline [" + pipelineId + "] can't change the target index")
                        );
                        
                        onFinished.onResponse(null);
                        return;
                    } else {
                        indexRequest.isPipelineResolved(false);
                        resolvePipelines(null, indexRequest, state.metadata());
                        if (IngestService.NOOP_PIPELINE_NAME.equals(indexRequest.getFinalPipeline()) == false) {
                            newIt = Collections.singleton(indexRequest.getFinalPipeline()).iterator();