heesung-sn commented on PR #23903:
URL: https://github.com/apache/pulsar/pull/23903#issuecomment-2620587527
```java
package org.apache.bookkeeper.mledger.util;
import io.netty.util.Recycler;
import java.util.Collections;
import java.util.concurrent.locks.StampedLock;
import org.testng.annotations.Test;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class EntryWrapperPerformanceTestNG {
private static class EntryWrapper<K, V> {
private final Recycler.Handle<EntryWrapper> recyclerHandle;
private static final Recycler<EntryWrapper> RECYCLER = new
Recycler<EntryWrapper>() {
@Override
protected EntryWrapper newObject(Handle<EntryWrapper>
recyclerHandle) {
return new EntryWrapper(recyclerHandle);
}
};
private final StampedLock lock = new StampedLock();
private K key;
private V value;
long size;
private EntryWrapper(Recycler.Handle<EntryWrapper> recyclerHandle) {
this.recyclerHandle = recyclerHandle;
}
static <K, V> EntryWrapper<K, V> create(K key, V value, long size) {
EntryWrapper<K, V> entryWrapper = RECYCLER.get();
long stamp = entryWrapper.lock.writeLock();
entryWrapper.key = key;
entryWrapper.value = value;
entryWrapper.size = size;
entryWrapper.lock.unlockWrite(stamp);
return entryWrapper;
}
K getKey() {
long stamp = lock.tryOptimisticRead();
K localKey = key;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
localKey = key;
lock.unlockRead(stamp);
}
return localKey;
}
V getValue(K key) {
long stamp = lock.tryOptimisticRead();
K localKey = this.key;
V localValue = this.value;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
localKey = this.key;
localValue = this.value;
lock.unlockRead(stamp);
}
if (localKey != key) {
return null;
}
return localValue;
}
long getSize() {
long stamp = lock.tryOptimisticRead();
long localSize = size;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
localSize = size;
lock.unlockRead(stamp);
}
return localSize;
}
void recycle() {
key = null;
value = null;
size = 0;
recyclerHandle.recycle(this);
}
}
private static final int ITERATIONS = 1_000_000;
private static final int THREAD_COUNT = 10; // Simulating 10 concurrent
threads
private static final int TEST_RUNS = 100; // Run each test 10 times
private long getGCCount() {
long count = 0;
for (GarbageCollectorMXBean gcBean :
ManagementFactory.getGarbageCollectorMXBeans()) {
long gcCount = gcBean.getCollectionCount();
if (gcCount != -1) {
count += gcCount;
}
}
return count;
}
private long getUsedMemory() {
System.gc(); // Suggest GC before measuring memory usage
return Runtime.getRuntime().totalMemory() -
Runtime.getRuntime().freeMemory();
}
private void runTest(String testName, Runnable testLogic) {
List<Long> times = new ArrayList<>();
List<Long> gcCounts = new ArrayList<>();
List<Long> memoryUsages = new ArrayList<>();
for (int i = 0; i < TEST_RUNS; i++) {
long startGC = getGCCount();
long startMemory = getUsedMemory();
long startTime = System.nanoTime();
testLogic.run();
long elapsedTime = System.nanoTime() - startTime;
long endGC = getGCCount();
long endMemory = getUsedMemory();
times.add(elapsedTime);
gcCounts.add(endGC - startGC);
memoryUsages.add(endMemory - startMemory);
System.out.println(testName + " - Run " + (i + 1) + " - Time: "
+ TimeUnit.NANOSECONDS.toMillis(elapsedTime) + " ms, GC: " + (endGC - startGC)
+ ", Memory: " + (endMemory - startMemory) + " bytes");
}
printStatistics(testName, times, gcCounts, memoryUsages);
}
private void printStatistics(String testName, List<Long> times,
List<Long> gcCounts, List<Long> memoryUsages) {
System.out.println("========= " + testName + " Summary =========");
System.out.println("Average Time: " +
TimeUnit.NANOSECONDS.toMillis(average(times)) + " ms");
System.out.println("Median Time: " +
TimeUnit.NANOSECONDS.toMillis(median(times)) + " ms");
System.out.println("P99 Time: " +
TimeUnit.NANOSECONDS.toMillis(percentile(times, 99)) + " ms");
System.out.println("Max Time: " +
TimeUnit.NANOSECONDS.toMillis(Collections.max(times)) + " ms");
System.out.println("Average GC: " + average(gcCounts));
System.out.println("Median GC: " + median(gcCounts));
System.out.println("P99 GC: " + percentile(gcCounts, 99));
System.out.println("Max GC: " + Collections.max(gcCounts));
System.out.println("Average Memory: " + average(memoryUsages) + "
bytes");
System.out.println("Median Memory: " + median(memoryUsages) + "
bytes");
System.out.println("P99 Memory: " + percentile(memoryUsages, 99) + "
bytes");
System.out.println("Max Memory: " + Collections.max(memoryUsages) +
" bytes");
System.out.println("====================================");
}
private long average(List<Long> values) {
return values.stream().mapToLong(Long::longValue).sum() /
values.size();
}
private long median(List<Long> values) {
List<Long> sorted = new ArrayList<>(values);
Collections.sort(sorted);
int middle = sorted.size() / 2;
return (sorted.size() % 2 == 0) ? (sorted.get(middle - 1) +
sorted.get(middle)) / 2 : sorted.get(middle);
}
private long percentile(List<Long> values, int percentile) {
List<Long> sorted = new ArrayList<>(values);
Collections.sort(sorted);
int index = (int) Math.ceil(percentile / 100.0 * sorted.size()) - 1;
return sorted.get(Math.max(0, Math.min(index, sorted.size() - 1)));
}
@Test
public void testObjectPoolingPerformance() {
runTest("Object Pooling", () -> {
for (int i = 0; i < ITERATIONS; i++) {
EntryWrapper<Integer, String> entry = EntryWrapper.create(i,
"Value" + i, i);
entry.recycle();
}
});
}
@Test
public void testNewObjectPerformance() {
runTest("New Object Creation", () -> {
for (int i = 0; i < ITERATIONS; i++) {
var str = new String("Value" + i); // Creates a new String
object each time
}
});
}
@Test
public void testConcurrentObjectPooling() {
runTest("Concurrent Pooling", () -> {
ExecutorService executor =
Executors.newFixedThreadPool(THREAD_COUNT);
List<Future<?>> futures = new ArrayList<>();
for (int t = 0; t < THREAD_COUNT; t++) {
futures.add(executor.submit(() -> {
for (int i = 0; i < ITERATIONS / THREAD_COUNT; i++) {
EntryWrapper<Integer, String> entry =
EntryWrapper.create(i, "Value" + i, i);
entry.recycle();
}
}));
}
futures.forEach(f -> {
try {
f.get();
} catch (Exception ignored) {}
});
executor.shutdown();
});
}
@Test
public void testConcurrentNewObjectCreation() {
runTest("Concurrent New Object", () -> {
ExecutorService executor =
Executors.newFixedThreadPool(THREAD_COUNT);
List<Future<?>> futures = new ArrayList<>();
for (int t = 0; t < THREAD_COUNT; t++) {
futures.add(executor.submit(() -> {
//List<String> strings = new ArrayList<>();
for (int i = 0; i < ITERATIONS / THREAD_COUNT; i++) {
var str = new String("Value" + i); // Creates a new
String object each time
}
//strings.clear();
}));
}
futures.forEach(f -> {
try {
f.get();
} catch (Exception ignored) {}
});
executor.shutdown();
});
}
}
```
```
========= Concurrent New Object Summary =========
Average Time: 23 ms
Median Time: 22 ms
P99 Time: 32 ms
Max Time: 255 ms
Average GC: 4
Median GC: 4
P99 GC: 4
Max GC: 4
Average Memory: -20971 bytes
Median Memory: 0 bytes
P99 Memory: 0 bytes
Max Memory: 0 bytes
====================================
========= Concurrent Pooling Summary =========
Average Time: 40 ms
Median Time: 36 ms
P99 Time: 77 ms
Max Time: 439 ms
Average GC: 4
Median GC: 4
P99 GC: 4
Max GC: 4
Average Memory: -713031 bytes
Median Memory: 0 bytes
P99 Memory: 2097152 bytes
Max Memory: 2097152 bytes
====================================
========= New Object Creation Summary =========
Average Time: 10 ms
Median Time: 10 ms
P99 Time: 22 ms
Max Time: 40 ms
Average GC: 4
Median GC: 4
P99 GC: 4
Max GC: 4
Average Memory: -524288 bytes
Median Memory: 0 bytes
P99 Memory: 2097152 bytes
Max Memory: 2097152 bytes
====================================
========= Object Pooling Summary =========
Average Time: 54 ms
Median Time: 53 ms
P99 Time: 66 ms
Max Time: 92 ms
Average GC: 4
Median GC: 4
P99 GC: 4
Max GC: 4
Average Memory: -796917 bytes
Median Memory: 0 bytes
P99 Memory: 0 bytes
Max Memory: 2097152 bytes
====================================
```
Sharing some test result: object pooling vs jvm young gc performance test
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]