Skip to content

Commit 0aa4d9a

Browse files
authored
ParameterList - Cache the size and avoid iterator pattern (#1848)
I have been working on a very large monolith using Hibernate ORM and these particular methods were quite high in the profiling I made. Using this simple trick reduces things quite a lot, for what I think is an acceptable trade-off of the code looking a bit old school. FWIW, we have been fighting against iterator() in Quarkus in critical hot paths as it generates more allocations than simply going through the list. What is specific here is that size() is also quite slow in some cases, and it's also called by hasNext().
1 parent 9feca46 commit 0aa4d9a

File tree

1 file changed

+39
-12
lines changed

1 file changed

+39
-12
lines changed

byte-buddy-dep/src/main/java/net/bytebuddy/description/method/ParameterList.java

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ abstract class AbstractBase<S extends ParameterDescription> extends FilterableLi
7979
* {@inheritDoc}
8080
*/
8181
public boolean hasExplicitMetaData() {
82-
for (ParameterDescription parameterDescription : this) {
82+
// cache the size and make sure to avoid iterators here
83+
// this pattern reduces the number of allocations and also the CPU usage
84+
int size = size();
85+
for (int i = 0; i < size; i++) {
86+
ParameterDescription parameterDescription = get(i);
8387
if (!parameterDescription.isNamed() || !parameterDescription.hasModifiers()) {
8488
return false;
8589
}
@@ -91,9 +95,12 @@ public boolean hasExplicitMetaData() {
9195
* {@inheritDoc}
9296
*/
9397
public ByteCodeElement.Token.TokenList<ParameterDescription.Token> asTokenList(ElementMatcher<? super TypeDescription> matcher) {
94-
List<ParameterDescription.Token> tokens = new ArrayList<ParameterDescription.Token>(size());
95-
for (ParameterDescription parameterDescription : this) {
96-
tokens.add(parameterDescription.asToken(matcher));
98+
// cache the size and make sure to avoid iterators here
99+
// this pattern reduces the number of allocations and also the CPU usage
100+
int size = size();
101+
List<ParameterDescription.Token> tokens = new ArrayList<ParameterDescription.Token>(size);
102+
for (int i = 0; i < size; i++) {
103+
tokens.add(get(i).asToken(matcher));
97104
}
98105
return new ByteCodeElement.Token.TokenList<ParameterDescription.Token>(tokens);
99106
}
@@ -102,9 +109,12 @@ public ByteCodeElement.Token.TokenList<ParameterDescription.Token> asTokenList(E
102109
* {@inheritDoc}
103110
*/
104111
public TypeList.Generic asTypeList() {
105-
List<TypeDescription.Generic> types = new ArrayList<TypeDescription.Generic>(size());
106-
for (ParameterDescription parameterDescription : this) {
107-
types.add(parameterDescription.getType());
112+
// cache the size and make sure to avoid iterators here
113+
// this pattern reduces the number of allocations and also the CPU usage
114+
int size = size();
115+
List<TypeDescription.Generic> types = new ArrayList<TypeDescription.Generic>(size);
116+
for (int i = 0; i < size; i++) {
117+
types.add(get(i).getType());
108118
}
109119
return new TypeList.Generic.Explicit(types);
110120
}
@@ -113,9 +123,12 @@ public TypeList.Generic asTypeList() {
113123
* {@inheritDoc}
114124
*/
115125
public ParameterList<ParameterDescription.InDefinedShape> asDefined() {
116-
List<ParameterDescription.InDefinedShape> declaredForms = new ArrayList<ParameterDescription.InDefinedShape>(size());
117-
for (ParameterDescription parameterDescription : this) {
118-
declaredForms.add(parameterDescription.asDefined());
126+
// cache the size and make sure to avoid iterators here
127+
// this pattern reduces the number of allocations and also the CPU usage
128+
int size = size();
129+
List<ParameterDescription.InDefinedShape> declaredForms = new ArrayList<ParameterDescription.InDefinedShape>(size);
130+
for (int i = 0; i < size; i++) {
131+
declaredForms.add(get(i).asDefined());
119132
}
120133
return new Explicit<ParameterDescription.InDefinedShape>(declaredForms);
121134
}
@@ -143,6 +156,13 @@ abstract class ForLoadedExecutable<T> extends AbstractBase<ParameterDescription.
143156
*/
144157
protected final T executable;
145158

159+
/**
160+
* The number of parameters of this executable.
161+
* <p>
162+
* It is important to cache it as calling getParameterCount() via the dispatcher has a high cost.
163+
*/
164+
protected final int size;
165+
146166
/**
147167
* The parameter annotation source to query.
148168
*/
@@ -156,6 +176,7 @@ abstract class ForLoadedExecutable<T> extends AbstractBase<ParameterDescription.
156176
*/
157177
protected ForLoadedExecutable(T executable, ParameterDescription.ForLoadedParameter.ParameterAnnotationSource parameterAnnotationSource) {
158178
this.executable = executable;
179+
this.size = EXECUTABLE.getParameterCount(executable);
159180
this.parameterAnnotationSource = parameterAnnotationSource;
160181
}
161182

@@ -223,7 +244,7 @@ public static ParameterList<ParameterDescription.InDefinedShape> of(Method metho
223244
* {@inheritDoc}
224245
*/
225246
public int size() {
226-
return EXECUTABLE.getParameterCount(executable);
247+
return size;
227248
}
228249

229250
/**
@@ -562,6 +583,11 @@ class TypeSubstituting extends AbstractBase<ParameterDescription.InGenericShape>
562583
*/
563584
private final List<? extends ParameterDescription> parameterDescriptions;
564585

586+
/**
587+
* The number of parameters.
588+
*/
589+
private final int size;
590+
565591
/**
566592
* The visitor to apply to the parameter types before returning them.
567593
*/
@@ -579,6 +605,7 @@ public TypeSubstituting(MethodDescription.InGenericShape declaringMethod,
579605
TypeDescription.Generic.Visitor<? extends TypeDescription.Generic> visitor) {
580606
this.declaringMethod = declaringMethod;
581607
this.parameterDescriptions = parameterDescriptions;
608+
this.size = parameterDescriptions.size();
582609
this.visitor = visitor;
583610
}
584611

@@ -593,7 +620,7 @@ public ParameterDescription.InGenericShape get(int index) {
593620
* {@inheritDoc}
594621
*/
595622
public int size() {
596-
return parameterDescriptions.size();
623+
return size;
597624
}
598625
}
599626

0 commit comments

Comments
 (0)