HHH-15976 Further optimise InitializersList to avoid resizing collections

This commit is contained in:
Sanne Grinovero 2023-01-05 00:13:50 +01:00 committed by Sanne Grinovero
parent 04becd0aa4
commit 1e87b3399f
1 changed files with 69 additions and 62 deletions

View File

@ -7,7 +7,7 @@
package org.hibernate.sql.results.internal; package org.hibernate.sql.results.internal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -22,38 +22,36 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState;
* Internal helper to keep track of the various * Internal helper to keep track of the various
* Initializer instances being used during RowReader processing: * Initializer instances being used during RowReader processing:
* different types of initializers need to be invoked in different orders, * different types of initializers need to be invoked in different orders,
* so rather than finding them during each row we keep separated lists * so rather than having to identify the order of initializers again during
* of initializers defined upfront and then reused for the scope of the whole * the processing of each row we keep separated lists of initializers defined
* resultset. * upfront and then reuse these sets for the scope of the whole resultset's
* processing.
* @author Sanne Grinovero
*/ */
public final class InitializersList { public final class InitializersList {
private final List<Initializer> initializers;
private final List<Initializer> collectionInitializers; private final Initializer[] initializers;
private final List<Initializer> nonCollectionInitializers; private final Initializer[] sortedNonCollectionsFirst;
private final List<Initializer> resolveInstanceFirstInitializers; private final Initializer[] sortedForResolveInstance;
private final List<Initializer> resolveInstanceLaterInitializers;
private final boolean hasCollectionInitializers; private final boolean hasCollectionInitializers;
private final Map<NavigablePath, Initializer> initializerMap; private final Map<NavigablePath, Initializer> initializerMap;
private InitializersList( private InitializersList(
List<Initializer> initializers, Initializer[] initializers,
List<Initializer> collectionInitializers, Initializer[] sortedNonCollectionsFirst,
List<Initializer> nonCollectionInitializers, Initializer[] sortedForResolveInstance,
List<Initializer> resolveInstanceFirstInitializers, boolean hasCollectionInitializers,
List<Initializer> resolveInstanceLaterInitializers,
Map<NavigablePath, Initializer> initializerMap) { Map<NavigablePath, Initializer> initializerMap) {
this.initializers = initializers; this.initializers = initializers;
this.collectionInitializers = collectionInitializers; this.sortedNonCollectionsFirst = sortedNonCollectionsFirst;
this.nonCollectionInitializers = nonCollectionInitializers; this.sortedForResolveInstance = sortedForResolveInstance;
this.resolveInstanceFirstInitializers = resolveInstanceFirstInitializers; this.hasCollectionInitializers = hasCollectionInitializers;
this.resolveInstanceLaterInitializers = resolveInstanceLaterInitializers;
this.hasCollectionInitializers = ! collectionInitializers.isEmpty();
this.initializerMap = initializerMap; this.initializerMap = initializerMap;
} }
@Deprecated //for simpler migration to the new SPI @Deprecated //for simpler migration to the new SPI
public List<Initializer> asList() { public List<Initializer> asList() {
return initializers; return Arrays.asList( initializers );
} }
public Initializer resolveInitializer(final NavigablePath path) { public Initializer resolveInitializer(final NavigablePath path) {
@ -79,19 +77,13 @@ public final class InitializersList {
} }
public void resolveKeys(final RowProcessingState rowProcessingState) { public void resolveKeys(final RowProcessingState rowProcessingState) {
for ( Initializer init : nonCollectionInitializers ) { for ( Initializer init : sortedNonCollectionsFirst ) {
init.resolveKey( rowProcessingState );
}
for ( Initializer init : collectionInitializers ) {
init.resolveKey( rowProcessingState ); init.resolveKey( rowProcessingState );
} }
} }
public void resolveInstances(final RowProcessingState rowProcessingState) { public void resolveInstances(final RowProcessingState rowProcessingState) {
for ( Initializer init : resolveInstanceFirstInitializers ) { for ( Initializer init : sortedForResolveInstance ) {
init.resolveInstance( rowProcessingState );
}
for ( Initializer init : resolveInstanceLaterInitializers ) {
init.resolveInstance( rowProcessingState ); init.resolveInstance( rowProcessingState );
} }
} }
@ -101,53 +93,68 @@ public final class InitializersList {
} }
static class Builder { static class Builder {
private List<Initializer> initializers = new ArrayList<>(); private ArrayList<Initializer> initializers = new ArrayList<>();
private List<Initializer> collectionInitializers; int nonCollectionInitializersNum = 0;
private List<Initializer> nonCollectionInitializers; int resolveFirstNum = 0;
private List<Initializer> resolveInstanceFirstInitializers;
private List<Initializer> resolveInstanceLaterInitializers;
public Builder() {} public Builder() {}
public void addInitializer(Initializer initializer) { public void addInitializer(final Initializer initializer) {
initializers.add( initializer ); initializers.add( initializer );
if ( initializer.isCollectionInitializer() ) { //in this method we perform these checks merely to learn the sizing hints,
if ( collectionInitializers == null ) { //so to not need dynamically scaling collections.
collectionInitializers = new ArrayList<>(); //This implies performing both checks twice but since they're cheap it's preferrable
//to multiple allocations; not least this allows using arrays, which makes iteration
//cheaper during the row processing - which is very hot.
if ( !initializer.isCollectionInitializer() ) {
nonCollectionInitializersNum++;
} }
collectionInitializers.add( initializer ); if ( initializeFirst( initializer ) ) {
} resolveFirstNum++;
else {
if ( nonCollectionInitializers == null ) {
nonCollectionInitializers = new ArrayList<>();
}
nonCollectionInitializers.add( initializer );
}
if ( !( initializer instanceof EntityDelayedFetchInitializer ) && ! (initializer instanceof EntitySelectFetchInitializer ) ) {
if ( resolveInstanceFirstInitializers == null ) {
resolveInstanceFirstInitializers = new ArrayList<>();
}
resolveInstanceFirstInitializers.add( initializer );
}
else {
if ( resolveInstanceLaterInitializers == null ) {
resolveInstanceLaterInitializers = new ArrayList<>();
}
resolveInstanceLaterInitializers.add( initializer );
} }
} }
InitializersList build(Map<NavigablePath, Initializer> initializerMap) { private static boolean initializeFirst(final Initializer initializer) {
return !( initializer instanceof EntityDelayedFetchInitializer ) && !( initializer instanceof EntitySelectFetchInitializer );
}
InitializersList build(final Map<NavigablePath, Initializer> initializerMap) {
final int size = initializers.size();
final Initializer[] sortedNonCollectionsFirst = new Initializer[size];
final Initializer[] sortedForResolveInstance = new Initializer[size];
int nonCollectionIdx = 0;
int collectionIdx = nonCollectionInitializersNum;
int resolveFirstIdx = 0;
int resolveLaterIdx = resolveFirstNum;
final Initializer[] originalSortInitializers = toArray( initializers );
for ( Initializer initializer : originalSortInitializers ) {
if ( initializer.isCollectionInitializer() ) {
sortedNonCollectionsFirst[collectionIdx++] = initializer;
}
else {
sortedNonCollectionsFirst[nonCollectionIdx++] = initializer;
}
if ( initializeFirst( initializer ) ) {
sortedForResolveInstance[resolveFirstIdx++] = initializer;
}
else {
sortedForResolveInstance[resolveLaterIdx++] = initializer;
}
}
final boolean hasCollectionInitializers = ( nonCollectionInitializersNum != initializers.size() );
return new InitializersList( return new InitializersList(
initializers, originalSortInitializers,
collectionInitializers == null ? Collections.EMPTY_LIST : collectionInitializers, sortedNonCollectionsFirst,
nonCollectionInitializers == null ? Collections.EMPTY_LIST : nonCollectionInitializers, sortedForResolveInstance,
resolveInstanceFirstInitializers == null ? Collections.EMPTY_LIST : resolveInstanceFirstInitializers, hasCollectionInitializers,
resolveInstanceLaterInitializers == null ? Collections.EMPTY_LIST : resolveInstanceLaterInitializers,
initializerMap initializerMap
); );
} }
private Initializer[] toArray(final ArrayList<Initializer> initializers) {
return initializers.toArray( new Initializer[initializers.size()] );
}
} }
} }