From 79244c9c657423d5cf853952e6e00d1f687749ea Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 24 Jan 2020 15:11:01 +0100 Subject: [PATCH] Optimize SubstatementContext size Class layout of SubstatementContext contains a set of flags in StatementContextBase and SubstatementContext, both of which end up being padded -- wasting 2-10 bytes in the padding alone. Moving configuration/ignoreConfig/ignoreIfFeature to StatementContextBase and allocating them as individual bits allows us to eliminate internal losses in most cases, resulting in net savings of 8 bytes per instance in the common case (64bit VM), i.e. 4.5-8.3%. JIRA: YANGTOOLS-652 Change-Id: Ic63f75e9e8c1c25445bc7904c08a5691d1470b67 Signed-off-by: Robert Varga --- .../stmt/reactor/StatementContextBase.java | 150 ++++++++++++++---- .../stmt/reactor/SubstatementContext.java | 75 +-------- 2 files changed, 123 insertions(+), 102 deletions(-) diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java index f19a7a4cc3..daa1d68e0e 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java @@ -34,7 +34,6 @@ import java.util.Optional; import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; -import org.opendaylight.yangtools.util.OptionalBoolean; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.model.api.YangStmtMapping; @@ -43,9 +42,11 @@ import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace; import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; import org.opendaylight.yangtools.yang.model.api.meta.StatementSource; +import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory; import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; import org.opendaylight.yangtools.yang.parser.spi.meta.ImplicitParentAwareStatementSupport; +import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder; import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour; @@ -98,6 +99,22 @@ public abstract class StatementContextBase, E private static final Logger LOG = LoggerFactory.getLogger(StatementContextBase.class); + // Flag bit assignments + private static final int IS_SUPPORTED_BY_FEATURES = 0x01; + private static final int HAVE_SUPPORTED_BY_FEATURES = 0x02; + private static final int IS_CONFIGURATION = 0x04; + private static final int HAVE_CONFIGURATION = 0x08; + private static final int IS_IGNORE_CONFIG = 0x10; + private static final int HAVE_IGNORE_CONFIG = 0x20; + private static final int IS_IGNORE_IF_FEATURE = 0x40; + private static final int HAVE_IGNORE_IF_FEATURE = 0x80; + + // Have-and-set flag constants, also used as masks + private static final int SET_SUPPORTED_BY_FEATURES = HAVE_SUPPORTED_BY_FEATURES | IS_SUPPORTED_BY_FEATURES; + private static final int SET_CONFIGURATION = HAVE_CONFIGURATION | IS_CONFIGURATION; + private static final int SET_IGNORE_CONFIG = HAVE_IGNORE_CONFIG | IS_IGNORE_CONFIG; + private static final int SET_IGNORE_IF_FEATURE = HAVE_IGNORE_IF_FEATURE | IS_IGNORE_IF_FEATURE; + private final @NonNull StatementDefinitionContext definition; private final @NonNull StatementSourceReference statementDeclSource; private final StmtContext originalCtx; @@ -111,16 +128,18 @@ public abstract class StatementContextBase, E private List> effectOfStatement = ImmutableList.of(); private StatementMap substatements = StatementMap.empty(); - private boolean isSupportedToBuildEffective = true; private @Nullable ModelProcessingPhase completedPhase; private @Nullable D declaredInstance; private @Nullable E effectiveInstance; - // BooleanFields value - private byte supportedByFeatures; - + // Common state bits + private boolean isSupportedToBuildEffective = true; private boolean fullyDefined; + // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above booleans and + // hence improve memory layout. + private byte flags; + StatementContextBase(final StatementDefinitionContext def, final StatementSourceReference ref, final String rawArgument) { this.definition = requireNonNull(def); @@ -188,44 +207,35 @@ public abstract class StatementContextBase, E @Override public boolean isSupportedByFeatures() { - if (OptionalBoolean.isPresent(supportedByFeatures)) { - return OptionalBoolean.get(supportedByFeatures); + final int fl = flags & SET_SUPPORTED_BY_FEATURES; + if (fl != 0) { + return fl == SET_SUPPORTED_BY_FEATURES; } - if (isIgnoringIfFeatures()) { - supportedByFeatures = OptionalBoolean.of(true); + flags |= SET_SUPPORTED_BY_FEATURES; return true; } - final boolean isParentSupported = isParentSupportedByFeatures(); /* - * If parent is not supported, then this context is also not supported. - * So we do not need to check if-features statements of this context and - * we can return false immediately. + * If parent is supported, we need to check if-features statements of this context. */ - if (!isParentSupported) { - supportedByFeatures = OptionalBoolean.of(false); - return false; + if (isParentSupportedByFeatures()) { + // If the set of supported features has not been provided, all features are supported by default. + final Set supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class, + SupportedFeatures.SUPPORTED_FEATURES); + if (supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures)) { + flags |= SET_SUPPORTED_BY_FEATURES; + return true; + } } - /* - * If parent is supported, we need to check if-features statements of - * this context. - */ - // If the set of supported features has not been provided, all features are supported by default. - final Set supportedFeatures = getFromNamespace(SupportedFeaturesNamespace.class, - SupportedFeatures.SUPPORTED_FEATURES); - final boolean ret = supportedFeatures == null || StmtContextUtils.checkFeatureSupport(this, supportedFeatures); - supportedByFeatures = OptionalBoolean.of(ret); - return ret; + // Either parent is not supported or this statement is not supported + flags |= HAVE_SUPPORTED_BY_FEATURES; + return false; } protected abstract boolean isParentSupportedByFeatures(); - protected abstract boolean isIgnoringIfFeatures(); - - protected abstract boolean isIgnoringConfig(); - @Override public boolean isSupportedToBuildEffective() { return isSupportedToBuildEffective; @@ -844,6 +854,86 @@ public abstract class StatementContextBase, E return result; } + /** + * Config statements are not all that common which means we are performing a recursive search towards the root + * every time {@link #isConfiguration()} is invoked. This is quite expensive because it causes a linear search + * for the (usually non-existent) config statement. + * + *

+ * This method maintains a resolution cache, so once we have returned a result, we will keep on returning the same + * result without performing any lookups, solely to support {@link SubstatementContext#isConfiguration()}. + */ + final boolean isConfiguration(final StatementContextBase parent) { + if (isIgnoringConfig()) { + return true; + } + final int fl = flags & SET_CONFIGURATION; + if (fl != 0) { + return fl == SET_CONFIGURATION; + } + final StmtContext configStatement = StmtContextUtils.findFirstSubstatement(this, + ConfigStatement.class); + final boolean parentIsConfig = parent.isConfiguration(); + + final boolean isConfig; + if (configStatement != null) { + isConfig = configStatement.coerceStatementArgument(); + + // Validity check: if parent is config=false this cannot be a config=true + InferenceException.throwIf(isConfig && !parentIsConfig, getStatementSourceReference(), + "Parent node has config=false, this node must not be specifed as config=true"); + } else { + // If "config" statement is not specified, the default is the same as the parent's "config" value. + isConfig = parentIsConfig; + } + + // Resolved, make sure we cache this return + flags |= isConfig ? SET_CONFIGURATION : HAVE_CONFIGURATION; + return isConfig; + } + + protected abstract boolean isIgnoringConfig(); + + /** + * This method maintains a resolution cache for ignore config, so once we have returned a result, we will + * keep on returning the same result without performing any lookups. Exists only to support + * {@link SubstatementContext#isIgnoringConfig()}. + */ + final boolean isIgnoringConfig(final StatementContextBase parent) { + final int fl = flags & SET_IGNORE_CONFIG; + if (fl != 0) { + return fl == SET_IGNORE_CONFIG; + } + if (definition().isIgnoringConfig() || parent.isIgnoringConfig()) { + flags |= SET_IGNORE_CONFIG; + return true; + } + + flags |= HAVE_IGNORE_CONFIG; + return false; + } + + protected abstract boolean isIgnoringIfFeatures(); + + /** + * This method maintains a resolution cache for ignore if-feature, so once we have returned a result, we will + * keep on returning the same result without performing any lookups. Exists only to support + * {@link SubstatementContext#isIgnoringIfFeatures()}. + */ + final boolean isIgnoringIfFeatures(final StatementContextBase parent) { + final int fl = flags & SET_IGNORE_IF_FEATURE; + if (fl != 0) { + return fl == SET_IGNORE_IF_FEATURE; + } + if (definition().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) { + flags |= SET_IGNORE_IF_FEATURE; + return true; + } + + flags |= HAVE_IGNORE_IF_FEATURE; + return false; + } + final void copyTo(final StatementContextBase target, final CopyType typeOfCopy, @Nullable final QNameModule targetModule) { final Collection> buffer = new ArrayList<>(substatements.size() + effective.size()); diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java index e70a5841ca..205fb36fdb 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java @@ -11,7 +11,6 @@ import static java.util.Objects.requireNonNull; import com.google.common.base.Verify; import java.util.Optional; -import org.opendaylight.yangtools.util.OptionalBoolean; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.YangVersion; @@ -19,14 +18,12 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.AugmentStatement; -import org.opendaylight.yangtools.yang.model.api.stmt.ConfigStatement; import org.opendaylight.yangtools.yang.model.api.stmt.DeviationStatement; import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier; import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; -import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException; import org.opendaylight.yangtools.yang.parser.spi.meta.MutableStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.NamespaceStorageNode; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry; @@ -40,32 +37,6 @@ final class SubstatementContext, E extends Eff private final StatementContextBase parent; private final A argument; - /** - * Config statements are not all that common which means we are performing a recursive search towards the root - * every time {@link #isConfiguration()} is invoked. This is quite expensive because it causes a linear search - * for the (usually non-existent) config statement. - * - *

- * This field maintains a resolution cache, so once we have returned a result, we will keep on returning the same - * result without performing any lookups. - */ - // BooleanField value - private byte configuration; - - /** - * This field maintains a resolution cache for ignore config, so once we have returned a result, we will - * keep on returning the same result without performing any lookups. - */ - // BooleanField value - private byte ignoreConfig; - - /** - * This field maintains a resolution cache for ignore if-feature, so once we have returned a result, we will - * keep on returning the same result without performing any lookups. - */ - // BooleanField value - private byte ignoreIfFeature; - private volatile SchemaPath schemaPath; SubstatementContext(final StatementContextBase parent, final StatementDefinitionContext def, @@ -178,33 +149,7 @@ final class SubstatementContext, E extends Eff @Override public boolean isConfiguration() { - if (isIgnoringConfig()) { - return true; - } - - if (OptionalBoolean.isPresent(configuration)) { - return OptionalBoolean.get(configuration); - } - - final StmtContext configStatement = StmtContextUtils.findFirstSubstatement(this, - ConfigStatement.class); - final boolean parentIsConfig = parent.isConfiguration(); - - final boolean isConfig; - if (configStatement != null) { - isConfig = configStatement.coerceStatementArgument(); - - // Validity check: if parent is config=false this cannot be a config=true - InferenceException.throwIf(isConfig && !parentIsConfig, getStatementSourceReference(), - "Parent node has config=false, this node must not be specifed as config=true"); - } else { - // If "config" statement is not specified, the default is the same as the parent's "config" value. - isConfig = parentIsConfig; - } - - // Resolved, make sure we cache this return - configuration = OptionalBoolean.of(isConfig); - return isConfig; + return isConfiguration(parent); } @Override @@ -239,26 +184,12 @@ final class SubstatementContext, E extends Eff @Override protected boolean isIgnoringIfFeatures() { - if (OptionalBoolean.isPresent(ignoreIfFeature)) { - return OptionalBoolean.get(ignoreIfFeature); - } - - final boolean ret = definition().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures(); - ignoreIfFeature = OptionalBoolean.of(ret); - - return ret; + return isIgnoringIfFeatures(parent); } @Override protected boolean isIgnoringConfig() { - if (OptionalBoolean.isPresent(ignoreConfig)) { - return OptionalBoolean.get(ignoreConfig); - } - - final boolean ret = definition().isIgnoringConfig() || parent.isIgnoringConfig(); - ignoreConfig = OptionalBoolean.of(ret); - - return ret; + return isIgnoringConfig(parent); } @Override -- 2.36.6