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/IngestDocument.java
if (context instanceof Map) {
            @SuppressWarnings("unchecked")
            Map<String, Object> map = (Map<String, Object>) context;
            if (append) {
                if (map.containsKey(leafKey)) {
                    Object object = map.get(leafKey);
                    Object list = appendValues(object, value, allowDuplicates);
                    if (list != object) {
                        map.put(leafKey, list);
                    }
                } else {
                    List<Object> list = new ArrayList<>();
                    appendValues(list, value);
                    map.put(leafKey, list);
                }
                return;
            }
            map.put(leafKey, value);
        } else if (context instanceof List) {
            @SuppressWarnings("unchecked")
            List<Object> list = (List<Object>) context;
            int index;
            try {
                index = Integer.parseInt(leafKey);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException(
                    "[" + leafKey + "] is not an integer, cannot be used as an index as part of path [" + path + "]",
                    e
                );
            }