Currently 99% of a profiled `script` processor's execution time comes from the invocation of `CollectionUtils.ensureNoSelfReferences()`, not the script itself (this could vary with script intensity, but it stands to reason it would take a lot to change this dramatically).
<img width="2552" alt="image" src="https://user-images.githubusercontent.com/7477841/162812089-a8d9f023-abf5-4249-b676-78b2adc459a1.png">

1. Is there another way we could approach this self-reference guard rail? It's not completely fool-proof but after the first document passes this check we can be reasonably sure the Script doesn't cause self-references, except in the case of some very specific conditional behavior one would almost have to intentionally code in. As this measure is to help users prevent shooting themselves in the foot, an ostensible 100x overhead is too high a cost, surely.
2. As a half-measure, should we not execute the check if the processor has `ignore_errors` set to `true` anyway? This is the case even for some published integrations, and could benefit greatly from the reduced overhead. It's not ideal as this particular problem really should probably not be ignored since it will cause issues in a later step, but this would preserve existing behavior and reduce cost.
server/src/main/java/org/elasticsearch/ingest/IngestService.java
}
            try {
                Pipeline newPipeline = Pipeline.create(
                    newConfiguration.getId(),
                    newConfiguration.getConfigAsMap(),
                    processorFactories,
                    scriptService
                );
                newPipelines.put(newConfiguration.getId(), new PipelineHolder(newConfiguration, newPipeline));
                if (previous == null) {
                    continue;
                }
                Pipeline oldPipeline = previous.pipeline;
                newPipeline.getMetrics().add(oldPipeline.getMetrics());
                List<Tuple<Processor, IngestMetric>> oldPerProcessMetrics = new ArrayList<>();
                List<Tuple<Processor, IngestMetric>> newPerProcessMetrics = new ArrayList<>();
                getProcessorMetrics(oldPipeline.getCompoundProcessor(), oldPerProcessMetrics);
                getProcessorMetrics(newPipeline.getCompoundProcessor(), newPerProcessMetrics);
                
                
                
                if (newPerProcessMetrics.size() == oldPerProcessMetrics.size()) {
                    Iterator<Tuple<Processor, IngestMetric>> oldMetricsIterator = oldPerProcessMetrics.iterator();
                    for (Tuple<Processor, IngestMetric> compositeMetric : newPerProcessMetrics) {
                        String type = compositeMetric.v1().getType();
                        IngestMetric metric = compositeMetric.v2();
                        if (oldMetricsIterator.hasNext()) {
                            Tuple<Processor, IngestMetric> oldCompositeMetric = oldMetricsIterator.next();
                            String oldType = oldCompositeMetric.v1().getType();
                            IngestMetric oldMetric = oldCompositeMetric.v2();