From 577931e8bcf23d860e4fc20f6cf3c07ee354b83b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 28 Oct 2015 11:46:28 +0100 Subject: [PATCH] Use bitwise operation when sampling for resource leak detection. Motivation: Modulo operations are slow, we can use bitwise operation to detect if resource leak detection must be done while sampling. Modifications: - Ensure the interval is a power of two - Use bitwise operation for sampling - Add benchmark. Result: Faster sampling. --- .../java/io/netty/buffer/PoolThreadCache.java | 17 +------- .../io/netty/util/ResourceLeakDetector.java | 12 ++++-- .../java/io/netty/util/internal/MathUtil.java | 39 +++++++++++++++++++ .../util/ResourceLeakDetectorBenchmark.java | 36 +++++++++++++++++ 4 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 common/src/main/java/io/netty/util/internal/MathUtil.java create mode 100644 microbench/src/test/java/io/netty/microbench/util/ResourceLeakDetectorBenchmark.java diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java index 425f657ca9..def11d5800 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java +++ b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java @@ -21,6 +21,7 @@ import io.netty.buffer.PoolArena.SizeClass; import io.netty.util.Recycler; import io.netty.util.Recycler.Handle; import io.netty.util.ThreadDeathWatcher; +import io.netty.util.internal.MathUtil; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -356,25 +357,11 @@ final class PoolThreadCache { private int allocations; MemoryRegionCache(int size, SizeClass sizeClass) { - this.size = powerOfTwo(size); + this.size = MathUtil.findNextPositivePowerOfTwo(size); queue = PlatformDependent.newFixedMpscQueue(this.size); this.sizeClass = sizeClass; } - private static int powerOfTwo(int res) { - if (res <= 2) { - return 2; - } - res--; - res |= res >> 1; - res |= res >> 2; - res |= res >> 4; - res |= res >> 8; - res |= res >> 16; - res++; - return res; - } - /** * Init the {@link PooledByteBuf} using the provided chunk and handle with the capacity restrictions. */ diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index d96381bc86..c848cf37bd 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -16,6 +16,7 @@ package io.netty.util; +import io.netty.util.internal.MathUtil; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.logging.InternalLogger; @@ -105,7 +106,8 @@ public final class ResourceLeakDetector { } } - private static final int DEFAULT_SAMPLING_INTERVAL = 113; + // Should be power of two. + private static final int DEFAULT_SAMPLING_INTERVAL = 128; /** * @deprecated Use {@link #setLevel(Level)} instead. @@ -148,6 +150,7 @@ public final class ResourceLeakDetector { private final String resourceType; private final int samplingInterval; + private final int mask; private final long maxActive; private long active; private final AtomicBoolean loggedTooManyActive = new AtomicBoolean(); @@ -178,7 +181,10 @@ public final class ResourceLeakDetector { } this.resourceType = resourceType; - this.samplingInterval = samplingInterval; + this.samplingInterval = MathUtil.findNextPositivePowerOfTwo(samplingInterval); + // samplingInterval is a power of two so we calculate a mask that we can use to + // check if we need to do any leak detection or not. + mask = this.samplingInterval - 1; this.maxActive = maxActive; head.next = tail; @@ -198,7 +204,7 @@ public final class ResourceLeakDetector { } if (level.ordinal() < Level.PARANOID.ordinal()) { - if (leakCheckCnt ++ % samplingInterval == 0) { + if ((leakCheckCnt ++ & mask) == 0) { reportLeak(level); return new DefaultResourceLeak(obj); } else { diff --git a/common/src/main/java/io/netty/util/internal/MathUtil.java b/common/src/main/java/io/netty/util/internal/MathUtil.java new file mode 100644 index 0000000000..7b22bcf931 --- /dev/null +++ b/common/src/main/java/io/netty/util/internal/MathUtil.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project 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 io.netty.util.internal; + +/** + * Math utility methods. + */ +public final class MathUtil { + + private MathUtil() { + } + + /** + * Fast method of finding the next power of 2 greater than or equal to the supplied value. + * + * If the value is {@code <= 0} then 1 will be returned. + * + * This method is not suitable for {@link Integer#MIN_VALUE} or numbers greater than 2^30. + * + * @param value from which to search for next power of 2 + * @return The next power of 2 or the value itself if it is a power of 2 + */ + public static int findNextPositivePowerOfTwo(final int value) { + assert value > Integer.MIN_VALUE && value < 0x40000000; + return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); + } +} diff --git a/microbench/src/test/java/io/netty/microbench/util/ResourceLeakDetectorBenchmark.java b/microbench/src/test/java/io/netty/microbench/util/ResourceLeakDetectorBenchmark.java new file mode 100644 index 0000000000..be6414a9e1 --- /dev/null +++ b/microbench/src/test/java/io/netty/microbench/util/ResourceLeakDetectorBenchmark.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project 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 io.netty.microbench.util; + +import io.netty.util.ResourceLeakDetector; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Setup; + +public class ResourceLeakDetectorBenchmark extends AbstractMicrobenchmark { + + private static final Object DUMMY = new Object(); + private ResourceLeakDetector detector; + + @Setup + public void setup() { + detector = new ResourceLeakDetector(getClass(), 128, Long.MAX_VALUE); + } + + @Benchmark + public Object open() { + return detector.open(DUMMY); + } +}