Repository: ignite Updated Branches: refs/heads/ignite-1917 df518bdbd -> cbfd19a4d
IGNITE-1917: Added fast ID resolution. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/6e773a0d Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/6e773a0d Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/6e773a0d Branch: refs/heads/ignite-1917 Commit: 6e773a0d02d761243f83f832aeae9910c3e9e6ac Parents: df518bd Author: vozerov-gridgain <voze...@gridgain.com> Authored: Tue Nov 17 13:11:29 2015 +0300 Committer: vozerov-gridgain <voze...@gridgain.com> Committed: Tue Nov 17 13:11:29 2015 +0300 ---------------------------------------------------------------------- .../internal/portable/PortableSchema.java | 14 +- .../portable/PortableSchemaIntIntMap.java | 217 +++++++++++++++++++ 2 files changed, 226 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/6e773a0d/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 index 86ca5f8..0c49451 100644 --- 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 @@ -47,6 +47,8 @@ public class PortableSchema implements Externalizable { /** Map with ID to order. */ private HashMap<Integer, Integer> idToOrder; + private PortableSchemaIntIntMap fastIdToOrder; + /** IDs depending on order. */ private ArrayList<Integer> ids; @@ -108,6 +110,7 @@ public class PortableSchema implements Externalizable { id7 = iter.hasNext() ? iter.next() : 0; idToOrder = null; + fastIdToOrder = null; } else { inline = false; @@ -123,6 +126,8 @@ public class PortableSchema implements Externalizable { ids.add(fieldId); idToOrder.put(fieldId, i); } + + fastIdToOrder = new PortableSchemaIntIntMap(idToOrder); } } @@ -210,11 +215,8 @@ public class PortableSchema implements Externalizable { return ORDER_NOT_FOUND; } - else { - Integer order = idToOrder.get(id); - - return order != null ? order : ORDER_NOT_FOUND; - } + else + return fastIdToOrder.get(id); } /** {@inheritDoc} */ @@ -283,6 +285,8 @@ public class PortableSchema implements Externalizable { ids.add(fieldId); idToOrder.put(fieldId, i); } + + fastIdToOrder = new PortableSchemaIntIntMap(idToOrder); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/6e773a0d/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaIntIntMap.java ---------------------------------------------------------------------- diff --git a/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaIntIntMap.java b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaIntIntMap.java new file mode 100644 index 0000000..f554b30 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/portable/PortableSchemaIntIntMap.java @@ -0,0 +1,217 @@ +/* + * 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.Map; + +/** + * Map for fast access to field order by ID. + */ +// TODO: IGNITE-1917: Inline into schema. +public class PortableSchemaIntIntMap { + /** Minimum sensible size. */ + private static final int MIN_SIZE = 32; + + /** Empty cell. */ + private static final int EMPTY = 0; + + /** Data. */ + private final int[] data; + + /** Mask for index calculation. */ + private final int mask; + + /** + * Constructor. + * + * @param vals Values. + */ + public PortableSchemaIntIntMap(Map<Integer, Integer> vals) { + int size = Math.max(nextPowerOfTwo(vals.size()) << 2, MIN_SIZE); + + assert size > 0; + + ParseResult finalRes; + + ParseResult res1 = parse(vals, size); + + if (res1.collisions == 0) + finalRes = res1; + else { + ParseResult res2 = parse(vals, size * 2); + + // Failed to decrease aom + if (res2.collisions == 0) + finalRes = res2; + else + finalRes = parse(vals, size * 4); + } + + data = finalRes.data; + + mask = maskForPowerOfTwo(data.length / 2); + } + + /** + * Get order. + * + * @param id ID. + * @return Order. + */ + public int get(int id) { + int idx = (id & mask) << 1; + + int curId = data[idx]; + + if (id == curId) // Hit! + return data[idx + 1]; + else if (curId == EMPTY) // No such ID! + return PortableSchema.ORDER_NOT_FOUND; + else { + // Unlikely collision scenario. + for (int i = 2; i < data.length; i += 2) { + int newIdx = (idx + i) % data.length; + + assert newIdx < data.length - 1; + + curId = data[newIdx]; + + if (id == curId) + return data[newIdx + 1]; + else if (curId == EMPTY) + return PortableSchema.ORDER_NOT_FOUND; + } + + return PortableSchema.ORDER_NOT_FOUND; + } + } + + /** + * Parse values. + * + * @param vals Values. + * @param size Proposed result size. + * @return Parse result. + */ + private static ParseResult parse(Map<Integer, Integer> vals, int size) { + int mask = maskForPowerOfTwo(size); + + int totalSize = size * 2; + + int[] data = new int[totalSize]; + int collisions = 0; + + for (Map.Entry<Integer, Integer> val : vals.entrySet()) { + int id = val.getKey(); + int order = val.getValue(); + + assert id != 0; + + int idIdx = (id & mask) << 1; + + if (data[idIdx] == 0) { + // Found empty slot. + data[idIdx] = id; + data[idIdx + 1] = order; + } + else { + // Collision! + collisions++; + + boolean placeFound = false; + + for (int i = 2; i < totalSize; i += 2) { + int newIdIdx = (idIdx + i) % totalSize; + + if (data[newIdIdx] == 0) { + data[newIdIdx] = id; + data[newIdIdx + 1] = order; + + placeFound = true; + + break; + } + } + + assert placeFound : "Should always have a place for entry!"; + } + } + + return new ParseResult(data, collisions); + } + + /** + * Get next power of two which greater or equal to the given number. + * This implementation is not meant to be very efficient, so it is expected to be used relatively rare. + * + * @param val Number + * @return Nearest pow2. + */ + private static int nextPowerOfTwo(int val) { + int res = 1; + + while (res < val) + res = res << 1; + + if (res < 0) + throw new IllegalArgumentException("Value is too big to find positive pow2: " + val); + + return res; + } + + /** + * Calculate mask for the given value which is a power of two. + * + * @param val Value. + * @return Mask. + */ + private static int maskForPowerOfTwo(int val) { + int mask = 0; + int comparand = 1; + + while (comparand < val) { + mask |= comparand; + + comparand <<= 1; + } + + return mask; + } + + /** + * Result of map parsing. + */ + private static class ParseResult { + /** Data. */ + public int[] data; + + /** Collisions. */ + public int collisions; + + /** + * Constructor. + * + * @param data Data. + * @param collisions Collisions. + */ + public ParseResult(int[] data, int collisions) { + this.data = data; + this.collisions = collisions; + } + } +}