Skip to content

Commit

Permalink
Dagger build performance optimizations.
Browse files Browse the repository at this point in the history
This CL:

  1. Adds state to keep track of any generated monitoring modules that Dagger generates, and uses this state to eagerly defer processing of components to the next round. This is useful to avoid doing work in the current round which will likely end up getting deferred anyway due to referencing the monitoring module.
  2. Adds a cache for ComponentDescriptors similar to our caches for other descriptors to avoid recomputing these descriptors.

RELNOTES=N/A
PiperOrigin-RevId: 591092232
  • Loading branch information
bcorso authored and Dagger Team committed Dec 15, 2023
1 parent 8372c63 commit be77d24
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 8 deletions.
10 changes: 10 additions & 0 deletions java/dagger/internal/codegen/DelegateComponentProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
import dagger.internal.codegen.base.SourceFileGenerator;
import dagger.internal.codegen.base.SourceFileHjarGenerator;
import dagger.internal.codegen.binding.BindingGraphFactory;
import dagger.internal.codegen.binding.ComponentDescriptor;
import dagger.internal.codegen.binding.InjectBindingRegistry;
import dagger.internal.codegen.binding.MembersInjectionBinding;
import dagger.internal.codegen.binding.ModuleDescriptor;
import dagger.internal.codegen.binding.MonitoringModules;
import dagger.internal.codegen.binding.ProductionBinding;
import dagger.internal.codegen.binding.ProvisionBinding;
import dagger.internal.codegen.bindinggraphvalidation.BindingGraphValidationModule;
Expand Down Expand Up @@ -170,6 +172,14 @@ interface ProcessingRoundCacheModule {
@IntoSet
ClearableCache moduleDescriptorFactory(ModuleDescriptor.Factory cache);

@Binds
@IntoSet
ClearableCache componentDescriptorFactory(ComponentDescriptor.Factory cache);

@Binds
@IntoSet
ClearableCache monitoringModules(MonitoringModules cache);

@Binds
@IntoSet
ClearableCache bindingGraphFactory(BindingGraphFactory cache);
Expand Down
5 changes: 3 additions & 2 deletions java/dagger/internal/codegen/binding/BindingGraphFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ private LegacyBindingGraph createLegacyBindingGraph(
ImmutableSet.Builder<SubcomponentDeclaration> subcomponentDeclarations = ImmutableSet.builder();

// Collect transitive module bindings and multibinding declarations.
for (ModuleDescriptor moduleDescriptor : modules(componentDescriptor, parentResolver)) {
ImmutableSet<ModuleDescriptor> modules = modules(componentDescriptor, parentResolver);
for (ModuleDescriptor moduleDescriptor : modules) {
explicitBindingsBuilder.addAll(moduleDescriptor.bindings());
multibindingDeclarations.addAll(moduleDescriptor.multibindingDeclarations());
subcomponentDeclarations.addAll(moduleDescriptor.subcomponentDeclarations());
Expand Down Expand Up @@ -223,7 +224,7 @@ private LegacyBindingGraph createLegacyBindingGraph(
if (createFullBindingGraph) {
// Resolve the keys for all bindings in all modules, stripping any multibinding contribution
// identifier so that the multibinding itself is resolved.
modules(componentDescriptor, parentResolver).stream()
modules.stream()
.flatMap(module -> module.allBindingKeys().stream())
.map(Key::withoutMultibindingContributionIdentifier)
.forEach(requestResolver::resolve);
Expand Down
18 changes: 17 additions & 1 deletion java/dagger/internal/codegen/binding/ComponentDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import static dagger.internal.codegen.base.ComponentCreatorAnnotation.creatorAnnotationsFor;
import static dagger.internal.codegen.base.ModuleAnnotation.moduleAnnotation;
import static dagger.internal.codegen.base.Scopes.productionScope;
import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent;
import static dagger.internal.codegen.binding.ConfigurationAnnotations.enclosedAnnotatedTypes;
import static dagger.internal.codegen.binding.ConfigurationAnnotations.isSubcomponentCreator;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
Expand Down Expand Up @@ -57,6 +58,7 @@
import dagger.Component;
import dagger.Module;
import dagger.Subcomponent;
import dagger.internal.codegen.base.ClearableCache;
import dagger.internal.codegen.base.ComponentAnnotation;
import dagger.internal.codegen.base.DaggerSuperficialValidation;
import dagger.internal.codegen.base.ModuleAnnotation;
Expand All @@ -70,6 +72,7 @@
import java.util.Optional;
import java.util.stream.Stream;
import javax.inject.Inject;
import javax.inject.Singleton;

/**
* A component declaration.
Expand Down Expand Up @@ -388,12 +391,14 @@ static boolean isComponentProductionMethod(XMethodElement method) {
}

/** A factory for creating a {@link ComponentDescriptor}. */
public static final class Factory {
@Singleton
public static final class Factory implements ClearableCache {
private final XProcessingEnv processingEnv;
private final DependencyRequestFactory dependencyRequestFactory;
private final ModuleDescriptor.Factory moduleDescriptorFactory;
private final InjectionAnnotations injectionAnnotations;
private final DaggerSuperficialValidation superficialValidation;
private final Map<XTypeElement, ComponentDescriptor> cache = new HashMap<>();

@Inject
Factory(
Expand Down Expand Up @@ -437,6 +442,12 @@ public ComponentDescriptor moduleComponentDescriptor(XTypeElement typeElement) {

private ComponentDescriptor create(
XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
return reentrantComputeIfAbsent(
cache, typeElement, unused -> createUncached(typeElement, componentAnnotation));
}

private ComponentDescriptor createUncached(
XTypeElement typeElement, ComponentAnnotation componentAnnotation) {
ImmutableSet<ComponentRequirement> componentDependencies =
componentAnnotation.dependencyTypes().stream()
.map(ComponentRequirement::forDependency)
Expand Down Expand Up @@ -572,5 +583,10 @@ private ComponentMethodDescriptor getDescriptorForComponentMethod(

return descriptor.build();
}

@Override
public void clearCache() {
cache.clear();
}
}
}
46 changes: 46 additions & 0 deletions java/dagger/internal/codegen/binding/MonitoringModules.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2023 The Dagger Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dagger.internal.codegen.binding;

import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.ClearableCache;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;

/** Keeps track of modules generated in the current round by {@link MonitoringModuleGenerator}. */
@Singleton
public final class MonitoringModules implements ClearableCache {
Set<ClassName> cache = new HashSet<>();

@Inject
MonitoringModules() {}

public void add(ClassName module) {
cache.add(module);
}

public boolean isEmpty() {
return cache.isEmpty();
}

@Override
public void clearCache() {
cache.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,23 @@
import com.squareup.javapoet.TypeSpec;
import dagger.Module;
import dagger.internal.codegen.base.SourceFileGenerator;
import dagger.internal.codegen.binding.MonitoringModules;
import dagger.internal.codegen.binding.SourceFiles;
import dagger.internal.codegen.javapoet.TypeNames;
import dagger.multibindings.Multibinds;
import javax.inject.Inject;

/** Generates a monitoring module for use with production components. */
final class MonitoringModuleGenerator extends SourceFileGenerator<XTypeElement> {
private final MonitoringModules monitoringModules;

@Inject
MonitoringModuleGenerator(XFiler filer, XProcessingEnv processingEnv) {
MonitoringModuleGenerator(
XFiler filer,
XProcessingEnv processingEnv,
MonitoringModules monitoringModules) {
super(filer, processingEnv);
this.monitoringModules = monitoringModules;
}

@Override
Expand All @@ -55,6 +61,7 @@ public XElement originatingElement(XTypeElement componentElement) {

@Override
public ImmutableList<TypeSpec.Builder> topLevelTypes(XTypeElement componentElement) {
monitoringModules.add(SourceFiles.generatedMonitoringModuleName(componentElement));
return ImmutableList.of(
classBuilder(SourceFiles.generatedMonitoringModuleName(componentElement))
.addAnnotation(Module.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.common.collect.Maps;
import com.squareup.javapoet.ClassName;
import dagger.internal.codegen.base.DaggerSuperficialValidation.ValidationException;
import dagger.internal.codegen.binding.MonitoringModules;
import dagger.internal.codegen.compileroption.CompilerOptions;
import dagger.internal.codegen.xprocessing.XElements;
import java.util.ArrayList;
Expand All @@ -51,6 +52,7 @@ abstract class TypeCheckingProcessingStep<E extends XElement> implements XProces
@Inject XMessager messager;
@Inject CompilerOptions compilerOptions;
@Inject SuperficialValidator superficialValidator;
@Inject MonitoringModules monitoringModules;

@Override
public final ImmutableSet<String> annotations() {
Expand All @@ -70,6 +72,16 @@ public ImmutableSet<XElement> process(
.forEach(
(element, annotations) -> {
try {
if (this instanceof ComponentProcessingStep && !monitoringModules.isEmpty()) {
// If there were any monitoring modules generated by Dagger in this round then we
// should just defer processing the components until the next round. This is an
// optimization to avoid unnecessary processing of components that will likely
// need to be reprocessed next round anyway due to the missing module. We should
// be guaranteed that there will be a next round since the monitoring modules were
// generated in this round.
deferredElements.add(element);
return;
}
// The XBasicAnnotationProcessor only validates the element itself. However, we
// validate the enclosing type here to keep the previous behavior of
// BasicAnnotationProcessor, since Dagger still relies on this behavior.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,11 @@ public ProductionGraphValidationTest(CompilerMode compilerMode) {
.buildOrThrow())
.compile(
subject -> {
subject.hasErrorCount(2);
subject.hasErrorCount(1);
subject.hasErrorContaining(
"TestClass.A is a provision, which cannot depend on a production.")
.onSource(component)
.onLineContaining("class AModule");
subject.hasErrorContaining("test.TestClass.AModule has errors")
.onSource(component)
.onLineContaining("@ProductionComponent");
});
}

Expand Down

0 comments on commit be77d24

Please sign in to comment.