From dab6e4c7199c91aeac93fca5956a0877bc16dd8e Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Wed, 2 Dec 2020 14:28:30 +0100 Subject: [PATCH] Move flags to ReactorStmtCtx We have a bunch of bitwise flags we are using for various inference details. Unfortunately them being placed in StatementContextBase and ReactorStmtCtx layout result in 8 bytes being lost on 64bit w\o compressed oops. Move the flags and their methods to ReactorStmtCtx, reducing InferredStatementContext footprint from 160 bytes to 152 bytes (i.e. 5%). JIRA: YANGTOOLS-1184 Change-Id: I0c9787795fc67c1e979132f9244f06a7a7e3dc3e Signed-off-by: Robert Varga --- .../parser/stmt/reactor/ReactorStmtCtx.java | 194 ++++++++++++++++++ .../stmt/reactor/StatementContextBase.java | 178 +--------------- 2 files changed, 195 insertions(+), 177 deletions(-) diff --git a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java index 421d9e806b..760f7c35cf 100644 --- a/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java +++ b/yang/yang-parser-reactor/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ReactorStmtCtx.java @@ -14,19 +14,27 @@ import com.google.common.base.MoreObjects.ToStringHelper; import com.google.common.base.VerifyException; import java.util.Collection; import java.util.Map; +import java.util.Optional; +import java.util.Set; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.YangVersion; 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.meta.IdentifierNamespace; import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; +import org.opendaylight.yangtools.yang.model.api.stmt.ConfigEffectiveStatement; import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +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.MutableStatement; import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Registry; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext.Mutable; +import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; +import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace; +import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -95,6 +103,47 @@ abstract class ReactorStmtCtx, E extends Effec private @Nullable E effectiveInstance; + // Master flag controlling whether this context can yield an effective statement + // FIXME: investigate the mechanics that are being supported by this, as it would be beneficial if we can get rid + // of this flag -- eliminating the initial alignment shadow used by below gap-filler fields. + private boolean isSupportedToBuildEffective = true; + + // 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_IGNORE_IF_FEATURE = 0x04; + private static final int HAVE_IGNORE_IF_FEATURE = 0x08; + // Note: these four are related + private static final int IS_IGNORE_CONFIG = 0x10; + private static final int HAVE_IGNORE_CONFIG = 0x20; + private static final int IS_CONFIGURATION = 0x40; + private static final int HAVE_CONFIGURATION = 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; + // Note: implies SET_CONFIGURATION, allowing fewer bit operations to be performed + private static final int SET_IGNORE_CONFIG = HAVE_IGNORE_CONFIG | IS_IGNORE_CONFIG | SET_CONFIGURATION; + private static final int SET_IGNORE_IF_FEATURE = HAVE_IGNORE_IF_FEATURE | IS_IGNORE_IF_FEATURE; + + // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above boolean and + // hence improve memory layout. + private byte flags; + + // Flag for use with AbstractResumedStatement. This is hiding in the alignment shadow created by above boolean + // FIXME: move this out once we have JDK15+ + private boolean fullyDefined; + + ReactorStmtCtx() { + // Empty on purpose + } + + ReactorStmtCtx(final ReactorStmtCtx original) { + isSupportedToBuildEffective = original.isSupportedToBuildEffective; + fullyDefined = original.fullyDefined; + flags = original.flags; + } + // // // Common public interface contracts with simple mechanics. Please keep this in one logical block, so we do not end @@ -236,6 +285,151 @@ abstract class ReactorStmtCtx, E extends Effec abstract @NonNull E createEffective(); + // + // + // Flags-based mechanics. These include public interfaces as well as all the crud we have lurking in our alignment + // shadow. + // + // + + @Override + public final boolean isSupportedToBuildEffective() { + return isSupportedToBuildEffective; + } + + @Override + public final void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) { + this.isSupportedToBuildEffective = isSupportedToBuildEffective; + } + + @Override + public final boolean isSupportedByFeatures() { + final int fl = flags & SET_SUPPORTED_BY_FEATURES; + if (fl != 0) { + return fl == SET_SUPPORTED_BY_FEATURES; + } + if (isIgnoringIfFeatures()) { + flags |= SET_SUPPORTED_BY_FEATURES; + return true; + } + + /* + * If parent is supported, we need to check if-features statements of this context. + */ + 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; + } + } + + // Either parent is not supported or this statement is not supported + flags |= HAVE_SUPPORTED_BY_FEATURES; + return false; + } + + protected abstract boolean isParentSupportedByFeatures(); + + /** + * 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()}. + * + *

+ * Note: use of this method implies that {@link #isIgnoringConfig()} is realized with + * {@link #isIgnoringConfig(StatementContextBase)}. + */ + final boolean isConfiguration(final StatementContextBase parent) { + final int fl = flags & SET_CONFIGURATION; + if (fl != 0) { + return fl == SET_CONFIGURATION; + } + if (isIgnoringConfig(parent)) { + // Note: SET_CONFIGURATION has been stored in flags + return true; + } + + final boolean isConfig; + final Optional optConfig = findSubstatementArgument(ConfigEffectiveStatement.class); + if (optConfig.isPresent()) { + isConfig = optConfig.orElseThrow(); + if (isConfig) { + // Validity check: if parent is config=false this cannot be a config=true + InferenceException.throwIf(!parent.isConfiguration(), sourceReference(), + "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 = parent.isConfiguration(); + } + + // 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()}. + * + *

+ * Note: use of this method implies that {@link #isConfiguration()} is realized with + * {@link #isConfiguration(StatementContextBase)}. + */ + final boolean isIgnoringConfig(final StatementContextBase parent) { + final int fl = flags & SET_IGNORE_CONFIG; + if (fl != 0) { + return fl == SET_IGNORE_CONFIG; + } + if (definition().support().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().support().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) { + flags |= SET_IGNORE_IF_FEATURE; + return true; + } + + flags |= HAVE_IGNORE_IF_FEATURE; + return false; + } + + // These two exists only due to memory optimization, should live in AbstractResumedStatement + final boolean fullyDefined() { + return fullyDefined; + } + + final void setFullyDefined() { + fullyDefined = true; + } + // // // Reference counting mechanics start. Please keep these methods in one block for clarity. Note this does not 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 319031d812..1fe9c071de 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 @@ -28,7 +28,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Optional; -import java.util.Set; import java.util.stream.Stream; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; @@ -40,7 +39,6 @@ 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.stmt.AugmentStatement; -import org.opendaylight.yangtools.yang.model.api.stmt.ConfigEffectiveStatement; 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; @@ -48,7 +46,6 @@ import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement; 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; @@ -60,8 +57,6 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils; import org.opendaylight.yangtools.yang.parser.spi.source.ImplicitSubstatement; import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; -import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace; -import org.opendaylight.yangtools.yang.parser.spi.source.SupportedFeaturesNamespace.SupportedFeatures; import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.KeyedValueAddedListener; import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWithListeners.PredicateValueAddedListener; import org.slf4j.Logger; @@ -106,24 +101,6 @@ 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_IGNORE_IF_FEATURE = 0x04; - private static final int HAVE_IGNORE_IF_FEATURE = 0x08; - // Note: these four are related - private static final int IS_IGNORE_CONFIG = 0x10; - private static final int HAVE_IGNORE_CONFIG = 0x20; - private static final int IS_CONFIGURATION = 0x40; - private static final int HAVE_CONFIGURATION = 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; - // Note: implies SET_CONFIGURATION, allowing fewer bit operations to be performed - private static final int SET_IGNORE_CONFIG = HAVE_IGNORE_CONFIG | IS_IGNORE_CONFIG | SET_CONFIGURATION; - private static final int SET_IGNORE_IF_FEATURE = HAVE_IGNORE_IF_FEATURE | IS_IGNORE_IF_FEATURE; - private final CopyHistory copyHistory; // Note: this field can strictly be derived in InferredStatementContext, but it forms the basis of many of our // operations, hence we want to keep it close by. @@ -136,31 +113,16 @@ public abstract class StatementContextBase, E private @Nullable ModelProcessingPhase completedPhase; - // Master flag controlling whether this context can yield an effective statement - // FIXME: investigate the mechanics that are being supported by this, as it would be beneficial if we can get rid - // of this flag -- eliminating the initial alignment shadow used by below gap-filler fields. - private boolean isSupportedToBuildEffective = true; - - // Flag for use with AbstractResumedStatement. This is hiding in the alignment shadow created by above boolean - private boolean fullyDefined; - - // Flags for use with SubstatementContext. These are hiding in the alignment shadow created by above boolean and - // hence improve memory layout. - private byte flags; - // SchemaPath cache for use with SubstatementContext and InferredStatementContext. This hurts RootStatementContext // a bit in terms of size -- but those are only a few and SchemaPath is on its way out anyway. private volatile SchemaPath schemaPath; // Copy constructor used by subclasses to implement reparent() StatementContextBase(final StatementContextBase original) { + super(original); this.copyHistory = original.copyHistory; this.definition = original.definition; - - this.isSupportedToBuildEffective = original.isSupportedToBuildEffective; - this.fullyDefined = original.fullyDefined; this.completedPhase = original.completedPhase; - this.flags = original.flags; } StatementContextBase(final StatementDefinitionContext def) { @@ -198,47 +160,6 @@ public abstract class StatementContextBase, E effectOfStatement.addAll(ctxs); } - @Override - public boolean isSupportedByFeatures() { - final int fl = flags & SET_SUPPORTED_BY_FEATURES; - if (fl != 0) { - return fl == SET_SUPPORTED_BY_FEATURES; - } - if (isIgnoringIfFeatures()) { - flags |= SET_SUPPORTED_BY_FEATURES; - return true; - } - - /* - * If parent is supported, we need to check if-features statements of this context. - */ - 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; - } - } - - // Either parent is not supported or this statement is not supported - flags |= HAVE_SUPPORTED_BY_FEATURES; - return false; - } - - protected abstract boolean isParentSupportedByFeatures(); - - @Override - public boolean isSupportedToBuildEffective() { - return isSupportedToBuildEffective; - } - - @Override - public void setIsSupportedToBuildEffective(final boolean isSupportedToBuildEffective) { - this.isSupportedToBuildEffective = isSupportedToBuildEffective; - } - @Override public CopyHistory getCopyHistory() { return copyHistory; @@ -439,15 +360,6 @@ public abstract class StatementContextBase, E return effective.isEmpty() ? new ArrayList<>(toAdd) : effective; } - // These two exists only due to memory optimization, should live in AbstractResumedStatement - final boolean fullyDefined() { - return fullyDefined; - } - - final void setFullyDefined() { - fullyDefined = true; - } - // Exposed for ReplicaStatementContext @Override E createEffective() { @@ -826,94 +738,6 @@ public abstract class StatementContextBase, E */ abstract boolean hasEmptySubstatements(); - /** - * 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()}. - * - *

- * Note: use of this method implies that {@link #isIgnoringConfig()} is realized with - * {@link #isIgnoringConfig(StatementContextBase)}. - */ - final boolean isConfiguration(final StatementContextBase parent) { - final int fl = flags & SET_CONFIGURATION; - if (fl != 0) { - return fl == SET_CONFIGURATION; - } - if (isIgnoringConfig(parent)) { - // Note: SET_CONFIGURATION has been stored in flags - return true; - } - - final boolean isConfig; - final Optional optConfig = findSubstatementArgument(ConfigEffectiveStatement.class); - if (optConfig.isPresent()) { - isConfig = optConfig.orElseThrow(); - if (isConfig) { - // Validity check: if parent is config=false this cannot be a config=true - InferenceException.throwIf(!parent.isConfiguration(), sourceReference(), - "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 = parent.isConfiguration(); - } - - // 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()}. - * - *

- * Note: use of this method implies that {@link #isConfiguration()} is realized with - * {@link #isConfiguration(StatementContextBase)}. - */ - final boolean isIgnoringConfig(final StatementContextBase parent) { - final int fl = flags & SET_IGNORE_CONFIG; - if (fl != 0) { - return fl == SET_IGNORE_CONFIG; - } - if (definition.support().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.support().isIgnoringIfFeatures() || parent.isIgnoringIfFeatures()) { - flags |= SET_IGNORE_IF_FEATURE; - return true; - } - - flags |= HAVE_IGNORE_IF_FEATURE; - return false; - } - abstract @NonNull Optional schemaPath(); // Exists only to support {SubstatementContext,InferredStatementContext}.schemaPath() -- 2.36.6