Skip to content

Commit

Permalink
IGNITE-10877 Reduce memory footprint and allocation pressure of affin…
Browse files Browse the repository at this point in the history
…ity assignments - Fixes #5796
  • Loading branch information
voropava authored and agoncharuk committed Jan 20, 2019
1 parent b324c04 commit 22c0c7e
Show file tree
Hide file tree
Showing 16 changed files with 1,316 additions and 90 deletions.
6 changes: 6 additions & 0 deletions modules/benchmarks/pom.xml
Expand Up @@ -51,6 +51,12 @@
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>

<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
Expand Down
@@ -0,0 +1,154 @@
/*
* 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.benchmarks.jmh.collections;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import org.apache.ignite.internal.benchmarks.jmh.JmhAbstractBenchmark;
import org.apache.ignite.internal.benchmarks.jmh.runner.JmhIdeBenchmarkRunner;
import org.apache.ignite.internal.benchmarks.model.Node;
import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.lang.IgniteClosure;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.openjdk.jmh.annotations.Mode.Throughput;

/**
* Comparison of HashMap vs view on List on small sizes.
*/
@State(Scope.Benchmark)
@OutputTimeUnit(NANOSECONDS)
@BenchmarkMode(Throughput)
public class SmallHashSetsVsReadOnlyViewBenchmark extends JmhAbstractBenchmark {
/** */
private static final int SIZE = AffinityAssignment.IGNITE_AFFINITY_BACKUPS_THRESHOLD;

/** */
private static final int PARTS = 8192;

/**
*
* @param args Args.
* @throws Exception Exception.
*/
public static void main(String[] args) throws Exception {
JmhIdeBenchmarkRunner.create()
.threads(1)
.measurementIterations(20)
.benchmarks(SmallHashSetsVsReadOnlyViewBenchmark.class.getSimpleName())
.run();
}

/** */
private final Random random = new Random();

/** */
private final List<Collection<UUID>> hashSets = new ArrayList<>();

/** */
private final List<List<Node>> lists = new ArrayList<>();

/** */
private final Node[] nodes = new Node[SIZE];

/** */
@Setup
public void setup() {
for (int i = 0; i < SIZE; i++)
nodes[i] = new Node(UUID.randomUUID());

for (int i= 0; i < PARTS; i++) {
Collection<UUID> hashSet = new HashSet<>();

for (int j = 0; j < SIZE; j++)
hashSet.add(nodes[j].getUuid());

hashSets.add(hashSet);

List<Node> list = new ArrayList<>(SIZE);

for (int j = 0; j < SIZE; j++)
list.add(nodes[j]);

lists.add(list);
}
}

/** */
@Benchmark
public boolean hashSetContainsRandom() {
return hashSets.get(random.nextInt(PARTS))
.contains(nodes[random.nextInt(SIZE)].getUuid());
}

/** */
@Benchmark
public boolean readOnlyViewContainsRandom() {
return F.viewReadOnly(
lists.get(random.nextInt(PARTS)),
(IgniteClosure<Node, UUID>)Node::getUuid
).contains(nodes[random.nextInt(SIZE)].getUuid());
}

/** */
@Benchmark
public boolean hashSetIteratorRandom() {
UUID randomUuid = nodes[random.nextInt(SIZE)].getUuid();

Collection<UUID> col = hashSets.get(random.nextInt(PARTS));

boolean contains = false;

for(UUID uuid : col)
if (randomUuid.equals(uuid))
contains = true;

return contains;
}

/** */
@Benchmark
public boolean readOnlyViewIteratorRandom() {
UUID randomUuid = nodes[random.nextInt(SIZE)].getUuid();

Collection<UUID> col = F.viewReadOnly(
lists.get(random.nextInt(PARTS)),
(IgniteClosure<Node, UUID>)Node::getUuid
);

boolean contains = false;

for(UUID uuid : col)
if (randomUuid.equals(uuid))
contains = true;

return contains;
}
}

@@ -0,0 +1,177 @@
/*
* 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.benchmarks.jol;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.ignite.cache.CacheMetrics;
import org.apache.ignite.cache.affinity.AffinityFunctionContext;
import org.apache.ignite.cache.affinity.rendezvous.RendezvousAffinityFunction;
import org.apache.ignite.cluster.ClusterMetrics;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.events.DiscoveryEvent;
import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.affinity.GridAffinityAssignment;
import org.apache.ignite.internal.processors.affinity.GridAffinityFunctionContextImpl;
import org.apache.ignite.lang.IgniteProductVersion;
import org.apache.ignite.spi.discovery.DiscoveryMetricsProvider;
import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode;
import org.openjdk.jol.info.GraphLayout;

/**
*
*/
public class GridAffinityAssignmentJolBenchmark {
/** */
private static DiscoveryMetricsProvider metrics = new DiscoveryMetricsProvider() {
@Override public ClusterMetrics metrics() {
return null;
}

@Override public Map<Integer, CacheMetrics> cacheMetrics() {
return null;
}
};

/** */
private static IgniteProductVersion ver = new IgniteProductVersion();

/** */
private static Field field;

/** */
public static void main(String[] args) throws Exception {
RendezvousAffinityFunction aff = new RendezvousAffinityFunction(true, 65000);

int[] parts = new int[] {1024, 8192, 32768, 65000};

int[] nodes = new int[] {1, 16, 160, 600};

// We need to implement compressed bitsets https://issues.apache.org/jira/browse/IGNITE-4554.
// On 65k partitions and nodes > 700 HashSet take advantage over BitSet.
// After implementation need to check consumption on big clusters.
for (int part : parts)
for (int node : nodes) {
measure(aff, part, node, 0);

measure(aff, part, node, 3);

measure(aff, part, node, node);
}
}

/**
* @param disabled Disabled.
*/
private static void setOptimization(boolean disabled) throws NoSuchFieldException, IllegalAccessException {
if (field == null) {
field = AffinityAssignment.class.getDeclaredField("IGNITE_DISABLE_AFFINITY_MEMORY_OPTIMIZATION");

Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

field.setAccessible(true);
}

field.set(null, disabled);
}

/**
* @param aff Aff.
* @param parts Parts.
* @param nodeCnt Node count.
* @param backups Backups.
*/
private static void measure(
RendezvousAffinityFunction aff,
int parts,
int nodeCnt,
int backups
) throws Exception {
List<ClusterNode> nodes = new ArrayList<>();

for (int i = 0; i < nodeCnt; i++) {
TcpDiscoveryNode node = new TcpDiscoveryNode(
UUID.randomUUID(),
Collections.singletonList("127.0.0.1"),
Collections.singletonList("127.0.0.1"),
0,
metrics,
ver,
i
);
node.setAttributes(new HashMap<>());
nodes.add(node);
}

AffinityFunctionContext ctx = new GridAffinityFunctionContextImpl(
nodes,
new ArrayList<>(),
new DiscoveryEvent(),
new AffinityTopologyVersion(),
backups
);

List<List<ClusterNode>> assignment = aff.assignPartitions(ctx);

setOptimization(false);

GridAffinityAssignment ga = new GridAffinityAssignment(
new AffinityTopologyVersion(1, 0),
assignment,
new ArrayList<>()
);

System.gc();

long totalSize = GraphLayout.parseInstance(ga).totalSize();

System.out.println("Optimized, parts " + parts
+" nodeCount " + nodeCnt
+" backups " + backups
+ " " + totalSize);

setOptimization(true);

GridAffinityAssignment ga2 = new GridAffinityAssignment(
new AffinityTopologyVersion(1, 0),
assignment,
new ArrayList<>()
);

System.gc();

long totalSize2 = GraphLayout.parseInstance(ga2).totalSize();

System.out.println("Deoptimized, parts " + parts
+" nodeCount " + nodeCnt
+" backups " + backups
+ " " + totalSize2);

if (totalSize > totalSize2)
throw new Exception("Optimized AffinityAssignment size " + totalSize +" is more than deoptimized" + totalSize2);
}
}
@@ -0,0 +1,44 @@
/*
* 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.benchmarks.model;

import java.util.UUID;

/**
*
*/
public class Node {
/** */
private UUID uuid;

/**
*
* @param uuid Uuid.
*/
public Node(UUID uuid) {
this.uuid = uuid;
}

/**
*
* @return UUID.
*/
public UUID getUuid() {
return uuid;
}
}

0 comments on commit 22c0c7e

Please sign in to comment.