package com.linkedin.databus.core.util; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * Licensed 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. * */ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.linkedin.databus2.test.TestUtil; public class TestReadWriteSyncedObjectPerf { public static final Logger LOG = Logger.getLogger(TestReadWriteSyncedObjectPerf.class); public static final int ITER_NUM = 10000000; public static class TestClass extends ReadWriteSyncedObject { private boolean _threadUnsafe; public int _counter1; public int _counter2; private AtomicInteger _counter3; private AtomicInteger _counter4; public volatile int _counter5; public volatile int _counter6; public TestClass(boolean threadSafe) { super(threadSafe); _threadUnsafe = ! threadSafe; _counter1 = 1; _counter2 = 1; _counter3 = new AtomicInteger(1); _counter4 = new AtomicInteger(1); } public int getCounter1ThreadedFinally() { Lock readLock = _threadUnsafe? null : acquireReadLock(); int result = 0; try { result = _counter1; } finally { releaseLock(readLock); } return result; } public int getCounter1ThreadedNoFinally() { Lock readLock = _threadUnsafe? null : acquireReadLock(); int result = _counter1; releaseLock(readLock); return result; } public int getCounter1ThreadedFinallyDirect() { Lock readLock = acquireReadLock(null); try { return _counter1; } finally { releaseLock(readLock); } } public int getCounter1ThreadedNoFinallyDirect() { Lock readLock = acquireReadLock(null); int result = _counter1; releaseLock(readLock); return result; } public int getCounter2ThreadedFinally() { Lock readLock = _threadUnsafe? null : acquireReadLock(); try { return _counter2; } finally { releaseLock(readLock); } } public int getCounter2ThreadedNoFinally() { Lock readLock = _threadUnsafe? null : acquireReadLock(); int result = _counter2; releaseLock(readLock); return result; } public int getCounter2ThreadedFinallyDirect() { Lock readLock = acquireReadLock(null); int result = 0; try { result = _counter2; } finally { releaseLock(readLock); } return result; } public int getCounter2ThreadedNoFinallyDirect() { Lock readLock = acquireReadLock(null); int result = _counter2; releaseLock(readLock); return result; } public int getCounter1NoLock() { return _threadUnsafe ? _counter1 : _counter1; } public int getCounter2NoLock() { return _threadUnsafe ? _counter2 : _counter2; } public int getCounter3() { return _counter3.get(); } public int getCounter4() { return _counter4.get(); } // MUTATORS public void incCounter1ThreadedFinally() { Lock writeLock = acquireWriteLock(); try { ++_counter1; } finally { releaseLock(writeLock); } } public void incCounter1ThreadedNoFinally() { Lock writeLock = acquireWriteLock(); ++ _counter1; releaseLock(writeLock); } public void incCounter1ThreadedFinallyDirect() { Lock writeLock = acquireWriteLock(null); try { ++_counter1; } finally { releaseLock(writeLock); } } public void incCounter1ThreadedNoFinallyDirect() { Lock writeLock = acquireWriteLock(null); ++_counter1; releaseLock(writeLock); } public void incCounter2ThreadedFinally() { Lock writeLock = acquireWriteLock(); try { ++ _counter2; } finally { releaseLock(writeLock); } } public void incCounter2ThreadedNoFinally() { Lock writeLock = acquireWriteLock(); ++_counter2; releaseLock(writeLock); } public void incCounter2ThreadedFinallyDirect() { Lock writeLock = acquireWriteLock(null); try { ++_counter2; } finally { releaseLock(writeLock); } } public void incCounter2ThreadedNoFinallyDirect() { Lock writeLock = acquireWriteLock(null); ++ _counter2; releaseLock(writeLock); } public void incCounter1NoLock() { ++_counter1; } public void incCounter2NoLock() { ++_counter2; } public void intCounter3() { _counter3.incrementAndGet(); } public void intCounter4() { _counter4.incrementAndGet(); } } @BeforeMethod public void setUp() throws Exception { TestUtil.setupLogging(true, null, Level.INFO); } @AfterMethod public void tearDown() throws Exception { } @Test public void testAcquireReadLock() { TestClass safeObject = new TestClass(true); TestClass unsafeObject = new TestClass(false); AssertJUnit.assertTrue(doReadTests("Reads thread-safe", safeObject) > 0); AssertJUnit.assertTrue(doReadTests("Reads thread-unsafe", unsafeObject) > 0); } private void doWriteTests(String expName, TestClass testObject) { testObject.incCounter1ThreadedFinally(); testObject.incCounter2ThreadedFinally(); //thread-safe finally easy long threadSafeFinallyEasyStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { testObject.incCounter1ThreadedFinally(); testObject.incCounter2ThreadedFinally(); } long threadSafeFinallyEasyFinish = System.nanoTime(); //thread-safe finally direct long threadSafeFinallyDirectStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { testObject.incCounter1ThreadedFinallyDirect(); testObject.incCounter2ThreadedFinallyDirect(); } long threadSafeFinallyDirectFinish = System.nanoTime(); //thread-safe finally direct long threadSafeNoFinallyDirectStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { testObject.incCounter1ThreadedNoFinallyDirect(); testObject.incCounter2ThreadedNoFinallyDirect(); } long threadSafeNoFinallyDirectFinish = System.nanoTime(); //thread-safe finally easy long threadSafeNoFinallyEasyStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { testObject.incCounter1ThreadedNoFinally(); testObject.incCounter2ThreadedNoFinally(); } long threadSafeNoFinallyEasyFinish = System.nanoTime(); //no-lock method long nolockMethodStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { testObject.incCounter1NoLock(); testObject.incCounter2NoLock(); } long nolockMethodFinish = System.nanoTime(); //no-lock attribute long nolockAttrStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { ++testObject._counter1; ++testObject._counter2; } long nolockAttrFinish = System.nanoTime(); //atomic method long atomicMethodStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { testObject.intCounter3(); testObject.intCounter4(); } long atomicMethodFinish = System.nanoTime(); //volatile method long volatileMethodStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { ++testObject._counter5; ++testObject._counter6; } long volatileMethodFinish = System.nanoTime(); LOG.info(String.format("%s (in ns/call)\n" + " finally direct : %f\n" + " finally easy : %f\n" + " no-finally direct : %f\n" + " no-finally easy : %f\n" + " no-lock method : %f\n" + " no-lock attribute : %f\n" + " atomic method : %f\n" + " volatile method : %f\n", expName, (threadSafeFinallyDirectFinish - threadSafeFinallyDirectStart) * 1.0 / ITER_NUM, (threadSafeFinallyEasyFinish - threadSafeFinallyEasyStart) * 1.0 / ITER_NUM, (threadSafeNoFinallyDirectFinish - threadSafeNoFinallyDirectStart) * 1.0 / ITER_NUM, (threadSafeNoFinallyEasyFinish - threadSafeNoFinallyEasyStart) * 1.0 / ITER_NUM, (nolockMethodFinish - nolockMethodStart) * 1.0 / ITER_NUM, (nolockAttrFinish - nolockAttrStart) * 1.0 / ITER_NUM, (atomicMethodFinish - atomicMethodStart) * 1.0 / ITER_NUM, (volatileMethodFinish - volatileMethodStart) * 1.0 / ITER_NUM)); } private int doReadTests(String expName, TestClass testObject) { int sum = testObject.getCounter1ThreadedFinally() + testObject.getCounter2ThreadedFinally(); //thread-safe finally easy long threadSafeFinallyEasyStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { sum += testObject.getCounter1ThreadedFinally() + testObject.getCounter2ThreadedFinally(); } long threadSafeFinallyEasyFinish = System.nanoTime(); //thread-safe finally direct long threadSafeFinallyDirectStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { sum += testObject.getCounter1ThreadedFinallyDirect() + testObject.getCounter2ThreadedFinallyDirect(); } long threadSafeFinallyDirectFinish = System.nanoTime(); //thread-safe finally direct long threadSafeNoFinallyDirectStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { sum += testObject.getCounter1ThreadedNoFinallyDirect() + testObject.getCounter2ThreadedNoFinallyDirect(); } long threadSafeNoFinallyDirectFinish = System.nanoTime(); //thread-safe finally easy long threadSafeNoFinallyEasyStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { sum += testObject.getCounter1ThreadedNoFinally() + testObject.getCounter2ThreadedNoFinally(); } long threadSafeNoFinallyEasyFinish = System.nanoTime(); //no-lock method long nolockMethodStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { sum += testObject.getCounter1NoLock() + testObject.getCounter2NoLock(); } long nolockMethodFinish = System.nanoTime(); //no-lock attribute long nolockAttrStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { sum += testObject._counter1 + testObject._counter2; } long nolockAttrFinish = System.nanoTime(); //no-lock method long atomicMethodStart = System.nanoTime(); for (int i = 0; i < ITER_NUM; ++i) { sum += testObject.getCounter3() + testObject.getCounter4(); } long atomicMethodFinish = System.nanoTime(); LOG.info(String.format("%s (in ns/call)\n" + " finally direct : %f\n" + " finally easy : %f\n" + " no-finally direct : %f\n" + " no-finally easy : %f\n" + " no-lock method : %f\n" + " no-lock attribute : %f\n" + " atomic method : %f\n", expName, (threadSafeFinallyDirectFinish - threadSafeFinallyDirectStart) * 1.0 / ITER_NUM, (threadSafeFinallyEasyFinish - threadSafeFinallyEasyStart) * 1.0 / ITER_NUM, (threadSafeNoFinallyDirectFinish - threadSafeNoFinallyDirectStart) * 1.0 / ITER_NUM, (threadSafeNoFinallyEasyFinish - threadSafeNoFinallyEasyStart) * 1.0 / ITER_NUM, (nolockMethodFinish - nolockMethodStart) * 1.0 / ITER_NUM, (nolockAttrFinish - nolockAttrStart) * 1.0 / ITER_NUM, (atomicMethodFinish - atomicMethodStart) * 1.0 / ITER_NUM)); return sum; } @Test public void testAcquireWriteLock() { TestClass safeObject = new TestClass(true); TestClass unsafeObject = new TestClass(false); //warm up - to trigger JIT compilation //don't run by default since it is time consuming // for (int i = 0; i < ITER_NUM * 100; ++i) // { // safeObject.incCounter1ThreadedFinally(); // safeObject.incCounter2ThreadedFinally(); // safeObject.intCounter3(); // } LOG.info("START THREAD-SAFE RUNS"); doWriteTests("Write thread-safe", safeObject); LOG.info("START THREAD-SAFE RUNS"); doWriteTests("Write thread-unsafe", unsafeObject); } }