Repository: ignite Updated Branches: refs/heads/ignite-1803 [created] 7ab23028b
IGNITE-1803: WIP. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/e80d69d8 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/e80d69d8 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/e80d69d8 Branch: refs/heads/ignite-1803 Commit: e80d69d88baa5bccaa061826964c36e204d25c26 Parents: 7eedab1 Author: vozerov-gridgain <[email protected]> Authored: Thu Oct 29 16:07:13 2015 +0300 Committer: vozerov-gridgain <[email protected]> Committed: Thu Oct 29 16:07:13 2015 +0300 ---------------------------------------------------------------------- .../internal/portable/PortableSchema.java | 223 +++++++++++++++++++ .../portable/PortableSchemaRegistry.java | 171 ++++++++++++++ .../apache/ignite/portable/PortableObject.java | 9 + .../portable/PortableObjectFieldDescriptor.java | 39 ++++ 4 files changed, 442 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/e80d69d8/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java new file mode 100644 index 0000000..09bfe35 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchema.java @@ -0,0 +1,223 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.ignite.internal.portable; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Schema describing portable object content. We rely on the following assumptions: + * - When amount of fields in the object is low, it is better to inline these values into int fields thus allowing + * for quick comparisons performed within already fetched L1 cache line. + * - When there are more fields, we store them inside a hash map. + */ +public class PortableSchema { + /** Inline flag. */ + private final boolean inline; + + /** Map with offsets. */ + private final HashMap<Integer, Integer> map; + + /** ID 1. */ + private final int id1; + + /** Offset 1. */ + private final int offset1; + + /** ID 2. */ + private final int id2; + + /** Offset 2. */ + private final int offset2; + + /** ID 3. */ + private final int id3; + + /** Offset 3. */ + private final int offset3; + + /** ID 4. */ + private final int id4; + + /** Offset 4. */ + private final int offset4; + + /** ID 1. */ + private final int id5; + + /** Offset 1. */ + private final int offset5; + + /** ID 2. */ + private final int id6; + + /** Offset 2. */ + private final int offset6; + + /** ID 3. */ + private final int id7; + + /** Offset 3. */ + private final int offset7; + + /** ID 4. */ + private final int id8; + + /** Offset 4. */ + private final int offset8; + + /** + * Constructor. + * + * @param vals Values. + */ + public PortableSchema(LinkedHashMap<Integer, Integer> vals) { + if (vals.size() <= 8) { + inline = true; + + Iterator<Map.Entry<Integer, Integer>> iter = vals.entrySet().iterator(); + + Map.Entry<Integer, Integer> entry = iter.hasNext() ? iter.next() : null; + + if (entry != null) { + id1 = entry.getKey(); + offset1 = entry.getValue(); + } + else{ + id1 = 0; + offset1 = 0; + } + + if ((entry = iter.hasNext() ? iter.next() : null) != null) { + id2 = entry.getKey(); + offset2 = entry.getValue(); + } + else{ + id2 = 0; + offset2 = 0; + } + + if ((entry = iter.hasNext() ? iter.next() : null) != null) { + id3 = entry.getKey(); + offset3 = entry.getValue(); + } + else{ + id3 = 0; + offset3 = 0; + } + + if ((entry = iter.hasNext() ? iter.next() : null) != null) { + id4 = entry.getKey(); + offset4 = entry.getValue(); + } + else{ + id4 = 0; + offset4 = 0; + } + + if ((entry = iter.hasNext() ? iter.next() : null) != null) { + id5 = entry.getKey(); + offset5 = entry.getValue(); + } + else{ + id5 = 0; + offset5 = 0; + } + + if ((entry = iter.hasNext() ? iter.next() : null) != null) { + id6 = entry.getKey(); + offset6 = entry.getValue(); + } + else{ + id6 = 0; + offset6 = 0; + } + + if ((entry = iter.hasNext() ? iter.next() : null) != null) { + id7 = entry.getKey(); + offset7 = entry.getValue(); + } + else{ + id7 = 0; + offset7 = 0; + } + + if ((entry = iter.hasNext() ? iter.next() : null) != null) { + id8 = entry.getKey(); + offset8 = entry.getValue(); + } + else{ + id8 = 0; + offset8 = 0; + } + + map = null; + } + else { + inline = false; + + id1 = id2 = id3 = id4 = id5 = id6 = id7 = id8 = 0; + offset1 = offset2 = offset3 = offset4 = offset5 = offset6 = offset7 = offset8 = 0; + + map = new HashMap<>(vals); + } + } + + /** + * Get offset for the given field ID. + * + * @param id Field ID. + * @return Offset or {@code 0} if there is no such field. + */ + public int offset(int id) { + if (inline) { + if (id == id1) + return offset1; + + if (id == id2) + return offset2; + + if (id == id3) + return offset3; + + if (id == id4) + return offset4; + + if (id == id5) + return offset5; + + if (id == id6) + return offset6; + + if (id == id7) + return offset7; + + if (id == id8) + return offset8; + + return 0; + } + else { + Integer off = map.get(id); + + return off != null ? off : 0; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/e80d69d8/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaRegistry.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaRegistry.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaRegistry.java new file mode 100644 index 0000000..88c3cce --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaRegistry.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.ignite.internal.portable; + +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; + +/** + * Portable schema registry. Contains all well-known object schemas. + * <p> + * We rely on the fact that usually object has only few different schemas. For this reason we inline several + * of them with optional fallback to normal hash map lookup. + * + */ +public class PortableSchemaRegistry { + /** Empty schema ID. */ + private static final int EMPTY = 0; + + /** Whether registry still works in inline mode. */ + private volatile boolean inline = true; + + /** First schema ID. */ + private int schemaId1; + + /** Second schema ID. */ + private int schemaId2; + + /** Third schema ID. */ + private int schemaId3; + + /** Fourth schema ID. */ + private int schemaId4; + + /** First schema. */ + private PortableSchema schema1; + + /** Second schema. */ + private PortableSchema schema2; + + /** Third schema. */ + private PortableSchema schema3; + + /** Fourth schema. */ + private PortableSchema schema4; + + /** Schemas with COW semantics. */ + private volatile HashMap<Integer, PortableSchema> schemas; + + /** + * Get schema for the given ID. We rely on very relaxed memory semantics here assuming that it is not critical + * to return false-positive {@code null} values. + * + * @param schemaId Schema ID. + * @return Schema or {@code null}. + */ + @Nullable public PortableSchema schema(int schemaId) { + if (inline) { + if (schemaId == schemaId1) + return schema1; + else if (schemaId == schemaId2) + return schema2; + else if (schemaId == schemaId3) + return schema3; + else if (schemaId == schemaId4) + return schema4; + } + else { + HashMap<Integer, PortableSchema> schemas0 = schemas; + + // Null can be observed here due to either data race or race condition when switching to non-inlined mode. + // Both of them are benign for us because they lead only to unnecessary schema re-calc. + if (schemas0 != null) + return schemas0.get(schemaId); + } + + return null; + } + + /** + * Add schema. + * + * @param schemaId Schema ID. + * @param schema Schema. + */ + public void addSchema(int schemaId, PortableSchema schema) { + synchronized (this) { + if (inline) { + // Check if this is already known schema. + if (schemaId == schemaId1 || schemaId == schemaId2 || schemaId == schemaId3 || schemaId == schemaId4) + return; + + // Try positioning new schema in inline mode. + if (schemaId1 == EMPTY) { + schemaId1 = schemaId; + + schema1 = schema; + + inline = true; // Forcing HB edge just in case. + + return; + } + + if (schemaId2 == EMPTY) { + schemaId2 = schemaId; + + schema2 = schema; + + inline = true; // Forcing HB edge just in case. + + return; + } + + if (schemaId3 == EMPTY) { + schemaId3 = schemaId; + + schema3 = schema; + + inline = true; // Forcing HB edge just in case. + + return; + } + + if (schemaId4 == EMPTY) { + schemaId4 = schemaId; + + schema4 = schema; + + inline = true; // Forcing HB edge just in case. + + return; + } + + // No luck, switching to hash map mode. + HashMap<Integer, PortableSchema> newSchemas = new HashMap<>(); + + newSchemas.put(schemaId1, schema1); + newSchemas.put(schemaId2, schema2); + newSchemas.put(schemaId3, schema3); + newSchemas.put(schemaId4, schema4); + newSchemas.put(schemaId, schema); + + schemas = newSchemas; + + inline = false; + } + else { + HashMap<Integer, PortableSchema> newSchemas = new HashMap<>(schemas); + + newSchemas.put(schemaId, schema); + + schemas = newSchemas; + } + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/e80d69d8/modules/core/src/main/java/org/apache/ignite/portable/PortableObject.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/portable/PortableObject.java b/modules/core/src/main/java/org/apache/ignite/portable/PortableObject.java index 66b8f76..08c6622 100644 --- a/modules/core/src/main/java/org/apache/ignite/portable/PortableObject.java +++ b/modules/core/src/main/java/org/apache/ignite/portable/PortableObject.java @@ -137,6 +137,15 @@ public interface PortableObject extends Serializable, Cloneable { public boolean hasField(String fieldName); /** + * Gets field descriptor. + * + * @param fieldName Field name. + * @return Field descriptor. + * @throws PortableException If failed. + */ + public PortableObjectFieldDescriptor fieldDescriptor(String fieldName) throws PortableException; + + /** * Gets fully deserialized instance of portable object. * * @return Fully deserialized instance of portable object. http://git-wip-us.apache.org/repos/asf/ignite/blob/e80d69d8/modules/core/src/main/java/org/apache/ignite/portable/PortableObjectFieldDescriptor.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/portable/PortableObjectFieldDescriptor.java b/modules/core/src/main/java/org/apache/ignite/portable/PortableObjectFieldDescriptor.java new file mode 100644 index 0000000..182233a --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/portable/PortableObjectFieldDescriptor.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.ignite.portable; + +/** + * Portable object field. Can be used to speed object field lookup. + */ +public interface PortableObjectFieldDescriptor { + /** + * Check whether field exists in the object. + * + * @param obj Object. + * @return {@code True} if exists. + */ + public boolean exists(PortableObject obj); + + /** + * Get field's value from the given object. + * + * @param obj Object. + * @return Value. + */ + public <T> T value(PortableObject obj); +}
