From 4162d5df19ab1298844413b9218c2f0d8a35205e Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 21 Sep 2021 13:36:00 +1000 Subject: [PATCH] ArC build time perf improvement This removes the need to iterate over all beans when performing resolution. For a project with 2k injection points this saves close to 0.5s on my desktop. --- .../quarkus/arc/processor/BeanDeployment.java | 24 +++++++++++++++++++ .../arc/processor/BeanResolverImpl.java | 12 ++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java index 0a814bda4d486..365210f050fe2 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanDeployment.java @@ -68,6 +68,7 @@ public class BeanDeployment { private final Map stereotypes; private final List beans; + private volatile Map> beansByType = null; private final List interceptors; private final List decorators; @@ -248,6 +249,7 @@ void init(Consumer bytecodeTransformerConsumer, List> additionalUnusedBeanExclusions) { long start = System.nanoTime(); + initBeanByTypeMap(); // Collect dependency resolution errors List errors = new ArrayList<>(); for (BeanInfo bean : beans) { @@ -279,11 +281,25 @@ void init(Consumer bytecodeTransformerConsumer, LOGGER.debugf("Removed %s beans, %s interceptors and %s decorators in %s ms", removedBeans.size(), removedInterceptors.size(), removedDecorators.size(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - removalStart)); + //we need to re-initialize it, so it does not contain removed beans + initBeanByTypeMap(); } buildContext.putInternal(BuildExtension.Key.REMOVED_BEANS.asString(), Collections.unmodifiableSet(removedBeans)); LOGGER.debugf("Bean deployment initialized in %s ms", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start)); } + private void initBeanByTypeMap() { + beansByType = new HashMap<>(); + for (var bean : beans) { + for (var beanType : bean.types) { + beansByType.computeIfAbsent(beanType, (s) -> new ArrayList<>()).add(bean); + } + } + for (var e : beansByType.entrySet()) { + e.setValue(Collections.unmodifiableList(e.getValue())); + } + } + private void removeUnusedComponents(Set declaresObserver, List> allUnusedExclusions, Set removedDecorators, Set removedInterceptors) { @@ -407,6 +423,14 @@ public Collection getBeans() { return Collections.unmodifiableList(beans); } + public Collection getBeansByType(Type type) { + var ret = beansByType.get(type); + if (ret == null) { + return Collections.emptyList(); + } + return ret; + } + public Collection getRemovedBeans() { return Collections.unmodifiableSet(removedBeans); } diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java index a085f58197861..3229791511537 100644 --- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java +++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/BeanResolverImpl.java @@ -9,6 +9,7 @@ import io.quarkus.arc.processor.InjectionPointInfo.TypeAndQualifiers; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -81,7 +82,11 @@ List resolve(TypeAndQualifiers typeAndQualifiers) { private List findMatching(TypeAndQualifiers typeAndQualifiers) { List resolved = new ArrayList<>(); - for (BeanInfo b : beanDeployment.getBeans()) { + //optimisation for the simple class case + Collection potentialBeans = typeAndQualifiers.type.kind() == CLASS + ? beanDeployment.getBeansByType(typeAndQualifiers.type) + : beanDeployment.getBeans(); + for (BeanInfo b : potentialBeans) { if (Beans.matches(b, typeAndQualifiers)) { resolved.add(b); } @@ -91,7 +96,10 @@ private List findMatching(TypeAndQualifiers typeAndQualifiers) { List findTypeMatching(Type type) { List resolved = new ArrayList<>(); - for (BeanInfo b : beanDeployment.getBeans()) { + //optimisation for the simple class case + Collection potentialBeans = type.kind() == CLASS ? beanDeployment.getBeansByType(type) + : beanDeployment.getBeans(); + for (BeanInfo b : potentialBeans) { if (Beans.matchesType(b, type)) { resolved.add(b); }