/**
* Copyright 2013-2015 Seagate Technology LLC.
*
* This Source Code Form is subject to the terms of the Mozilla
* Public License, v. 2.0. If a copy of the MPL was not
* distributed with this file, You can obtain one at
* https://mozilla.org/MP:/2.0/.
*
* This program is distributed in the hope that it will be useful,
* but is provided AS-IS, WITHOUT ANY WARRANTY; including without
* the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or
* FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public
* License for more details.
*
* See www.openkinetic.org for more project information
*/
package com.seagate.kinetic.simulator.performance;
import org.testng.annotations.Test;
import org.testng.Assert;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;
import kinetic.client.ClientConfiguration;
import kinetic.client.Entry;
import kinetic.client.EntryMetadata;
import kinetic.client.KineticClient;
import kinetic.client.KineticClientFactory;
import kinetic.client.KineticException;
import kinetic.simulator.KineticSimulator;
import kinetic.simulator.SimulatorConfiguration;
/**
*
* performance test, include nio client/server, sync and async permute.
* <p>
*
* @author Chenchong(Emma) Li
*
*/
public class Performance {
private static final Logger logger = Logger.getLogger(Performance.class
.getName());
private static final String INIT_VERSION = "0";
private static final int WARM_UP_COUNT = 5000;
private static ClientConfiguration clientConfig = null;
private static SimulatorConfiguration serverConfig = null;
private static KineticSimulator kineticServer = null;
private static KineticClient kineticClient = null;
private static KVGenerator kvGenerator = null;
private static CountDownLatch signal = null;
private static List<String> testResultInfo = null;
private static byte[] VALUE = null;
private static int VALUE_SIZE = 1024;
private static int OPERATE_COUNT = 10000;
private static int REPEAT_COUNT = 10;
private static int PORT = 8123;
public static void main(String[] args) throws InterruptedException {
if (args.length != 0 && args.length != 4) {
System.out.println("Parameters error!!!");
System.out.println("Usage:");
System.out
.println("PerfTest [Data_Size] [Operation_Count] [Repeat_Count] [Client_Port]");
System.out.println("Welcome to try again.");
return;
}
if (args.length > 0 && args.length == 4) {
VALUE_SIZE = Integer.parseInt(args[0]);
OPERATE_COUNT = Integer.parseInt(args[1]);
REPEAT_COUNT = Integer.parseInt(args[2]);
PORT = Integer.parseInt(args[3]);
System.out.println("Data_Size=" + VALUE_SIZE);
System.out.println("Operation_Count=" + OPERATE_COUNT);
System.out.println("Repeat_Count=" + REPEAT_COUNT);
System.out.println("Client_Port=" + PORT);
}
VALUE = ByteBuffer.allocate(VALUE_SIZE).array();
testResultInfo = new ArrayList<String>();
kvGenerator = new KVGenerator();
clientConfig = new ClientConfiguration(System.getProperties());
clientConfig.setPort(PORT);
boolean clientNioFlag;
boolean serverNioFlag;
// start server nio=f
serverNioFlag = false;
startServer(serverNioFlag);
// sync=f, nio(c/s)=f/f
clientNioFlag = false;
asyncPerf(clientNioFlag, serverNioFlag);
// sync=f, nio(c/s)=t/f
clientNioFlag = true;
asyncPerf(clientNioFlag, serverNioFlag);
// sync=t, nio(c/s)=f/f
clientNioFlag = false;
syncPerf(clientNioFlag, serverNioFlag);
// sync=t, nio(c/s)=t/f
clientNioFlag = true;
syncPerf(clientNioFlag, serverNioFlag);
closeServer();
// start server nio=t
serverNioFlag = true;
startServer(serverNioFlag);
// sync=f, nio(c/s)=f/t
clientNioFlag = false;
asyncPerf(clientNioFlag, serverNioFlag);
// sync=f, nio(c/s)=t/t
clientNioFlag = true;
asyncPerf(clientNioFlag, serverNioFlag);
// sync=t, nio(c/s)=f/t
clientNioFlag = false;
syncPerf(clientNioFlag, serverNioFlag);
// sync=t, nio(c/s)=t/t
clientNioFlag = true;
syncPerf(clientNioFlag, serverNioFlag);
closeServer();
printTestResult();
}
private static void asyncPerf(boolean clientNioFlag, boolean serverNioFlag)
throws InterruptedException {
try {
createClient(clientNioFlag);
// warm up
asyncWarmUp();
// Test begin
clear(OPERATE_COUNT, kineticClient);
long totalRepeatAsyncTime = 0;
for (int i = 0; i < REPEAT_COUNT; i++) {
kvGenerator.reset();
signal = new CountDownLatch(OPERATE_COUNT);
MyCallBackHandler myCallback = new MyCallBackHandler(signal);
long asyncStartTime = System.currentTimeMillis();
for (int j = 0; j < OPERATE_COUNT; j++) {
String key = kvGenerator.getNextKey();
// logger.info("adding data index=" + j);
EntryMetadata entryMetadata = new EntryMetadata();
Entry entryPut = new Entry(toByteArray(key), VALUE,
entryMetadata);
kineticClient.putAsync(entryPut,
toByteArray(INIT_VERSION), myCallback);
}
signal.await();
long asyncEndTime = System.currentTimeMillis();
long totalAsyncTime = asyncEndTime - asyncStartTime;
long messagePerSec = OPERATE_COUNT * 1000 / totalAsyncTime;
logger.info(testResult(false, clientNioFlag, serverNioFlag,
OPERATE_COUNT, totalAsyncTime, messagePerSec));
totalRepeatAsyncTime += totalAsyncTime;
clear(OPERATE_COUNT, kineticClient);
}
kineticClient.close();
long averageAsynTime = totalRepeatAsyncTime / REPEAT_COUNT;
long messagePerSec = OPERATE_COUNT * 1000 / averageAsynTime;
String testInfo = testResult(false, clientNioFlag, serverNioFlag,
OPERATE_COUNT, averageAsynTime, messagePerSec);
logger.info(testInfo);
testResultInfo.add(testInfo);
} catch (UnsupportedEncodingException e) {
logger.log(Level.SEVERE, e.getMessage());
} catch (KineticException e) {
logger.log(Level.SEVERE, e.getMessage());
}
}
private static void syncPerf(boolean clientNioFlag, boolean serverNioFlag) {
try {
createClient(clientNioFlag);
// warm up
syncWarmUp();
// Test begin
clear(OPERATE_COUNT, kineticClient);
long totalRepeatSyncTime = 0;
for (int i = 0; i < REPEAT_COUNT; i++) {
kvGenerator.reset();
long syncStartTime = System.currentTimeMillis();
for (int j = 0; j < OPERATE_COUNT; j++) {
String key = kvGenerator.getNextKey();
// logger.info("adding data index=" + j);
EntryMetadata entryMetadata = new EntryMetadata();
Entry entryPut = new Entry(toByteArray(key), VALUE,
entryMetadata);
kineticClient.put(entryPut, toByteArray(INIT_VERSION));
}
long syncEndTime = System.currentTimeMillis();
long totalSyncTime = syncEndTime - syncStartTime;
long messagePerSec = OPERATE_COUNT * 1000 / totalSyncTime;
logger.info(testResult(true, clientNioFlag, serverNioFlag,
OPERATE_COUNT, totalSyncTime, messagePerSec));
totalRepeatSyncTime += totalSyncTime;
clear(OPERATE_COUNT, kineticClient);
}
kineticClient.close();
long averageSynTime = totalRepeatSyncTime / REPEAT_COUNT;
long messagePerSec = OPERATE_COUNT * 1000 / averageSynTime;
String testInfo = testResult(true, clientNioFlag, serverNioFlag,
OPERATE_COUNT, averageSynTime, messagePerSec);
logger.info(testInfo);
testResultInfo.add(testInfo);
} catch (UnsupportedEncodingException e) {
logger.log(Level.SEVERE, e.getMessage());
} catch (KineticException e) {
logger.log(Level.SEVERE, e.getMessage());
}
}
/*
* async warm up
*/
private static void asyncWarmUp() throws UnsupportedEncodingException,
KineticException, InterruptedException {
// warm up
clear(WARM_UP_COUNT, kineticClient);
final CountDownLatch warmUpSignal = new CountDownLatch(WARM_UP_COUNT);
kvGenerator.reset();
MyCallBackHandler myWarmUpCallback = new MyCallBackHandler(warmUpSignal);
for (int warm = 0; warm < WARM_UP_COUNT; warm++) {
String key = kvGenerator.getNextKey();
// logger.info("adding warm data index=" + warm);
EntryMetadata entryMetadata = new EntryMetadata();
Entry entryPut = new Entry(toByteArray(key), VALUE, entryMetadata);
kineticClient.putAsync(entryPut, toByteArray(INIT_VERSION),
myWarmUpCallback);
}
warmUpSignal.await();
clear(WARM_UP_COUNT, kineticClient);
logger.info("Warm up number=" + WARM_UP_COUNT
+ ", warm up end, perf test begin!");
}
/*
* sync warm up
*/
private static void syncWarmUp() throws UnsupportedEncodingException,
KineticException {
clear(WARM_UP_COUNT, kineticClient);
kvGenerator.reset();
for (int warm = 0; warm < WARM_UP_COUNT; warm++) {
String key = kvGenerator.getNextKey();
// logger.info("adding warm data index=" + warm);
EntryMetadata entryMetadata = new EntryMetadata();
Entry entryPut = new Entry(toByteArray(key), VALUE, entryMetadata);
kineticClient.put(entryPut, toByteArray(INIT_VERSION));
}
clear(WARM_UP_COUNT, kineticClient);
logger.info("Warm up number=" + WARM_UP_COUNT
+ ", warm up end, perf test begin!");
}
private static KineticClient createClient(boolean clientNioFlag)
throws KineticException {
if (clientNioFlag) {
kineticClient = KineticClientFactory
.createInstance(clientConfig);
} else {
clientConfig.setUseNio(false);
kineticClient = KineticClientFactory
.createInstance(clientConfig);
}
return kineticClient;
}
private static void startServer(boolean serverNioFlag)
throws InterruptedException {
serverConfig = new SimulatorConfiguration();
serverConfig.put(SimulatorConfiguration.PERSIST_HOME, "performance");
serverConfig.setPort(PORT);
if (serverNioFlag) {
kineticServer = new KineticSimulator(serverConfig);
Thread.sleep(200);
} else {
serverConfig.setUseNio(serverNioFlag);
kineticServer = new KineticSimulator(serverConfig);
Thread.sleep(200);
}
}
private static void closeServer() {
if (null != kineticServer) {
kineticServer.close();
}
}
private static void printTestResult() {
if (!testResultInfo.isEmpty() && null != testResultInfo
&& 0 < testResultInfo.size()) {
for (int i = 0; i < testResultInfo.size(); i++) {
// logger.info(testResultInfo.get(i));
System.out.println(testResultInfo.get(i));
}
}
}
@Test(enabled = false)
private static String testResult(boolean syncFlag, boolean clientNioFlag,
boolean serverNioFlag, int operationCount, long totalAsyncTime,
long messagePerSec) {
String syncFlagS;
String clientNioFlagS;
String serverNioFlagS;
if (syncFlag) {
syncFlagS = "T";
} else {
syncFlagS = "F";
}
if (clientNioFlag) {
clientNioFlagS = "T";
} else {
clientNioFlagS = "F";
}
if (serverNioFlag) {
serverNioFlagS = "T";
} else {
serverNioFlagS = "F";
}
String resultInfo = "Sync=" + syncFlagS + ", Nio(C/S)="
+ clientNioFlagS + "/" + serverNioFlagS + ", Size="
+ VALUE_SIZE + "B, #OP=" + operationCount + ", Time="
+ totalAsyncTime + "ms, #Avg=" + messagePerSec + "op/sec";
return resultInfo;
}
private static void clear(int count, KineticClient kineticClient)
throws KineticException, UnsupportedEncodingException {
String key;
kvGenerator.reset();
for (int i = 0; i < count; i++) {
key = kvGenerator.getNextKey();
delete(kineticClient, toByteArray(key));
}
}
// clear all the versonedEntry
private static void delete(KineticClient kineticClient, byte[] key) {
Entry versionedEntry = null;
try {
versionedEntry = kineticClient.get(key);
} catch (KineticException e) {
Assert.fail("get key " + new String(key) + " failed, " + e.getMessage());
} catch (Exception e) {
Assert.fail("get key " + new String(key) + " failed, " + e.getMessage());
}
if (null != versionedEntry && null != versionedEntry.getKey()) {
try {
kineticClient.delete(versionedEntry);
// logger.info("delete key: " + new String(key));
} catch (KineticException e) {
Assert.fail("delete key " + new String(key) + " failed, "
+ e.getMessage());
} catch (Exception e) {
Assert.fail("delete key " + new String(key) + " failed, "
+ e.getMessage());
}
} else {
// logger.info("no key: " + new String(key));
}
}
// generator the key value, support reset and getNextKey and getValue
static class KVGenerator {
private String keyPrefix = "key";
private String valuePrefix = "value";
private int start = 0;
private int current = 0;
private final int alignLength = ("" + Integer.MAX_VALUE).length() + 1;
public KVGenerator() {
}
private String align(int id) {
String idAsString = "" + id;
int length = idAsString.length();
for (int i = 0; i < alignLength - length; i++) {
idAsString = "0" + idAsString;
}
return idAsString;
}
public KVGenerator(int start) {
this.start = start;
this.current = start;
}
public KVGenerator(String keyPrefix, String valuePrefix, int start) {
this.keyPrefix = keyPrefix;
this.valuePrefix = valuePrefix;
this.start = start;
this.current = start;
}
public void reset() {
this.current = start;
}
public synchronized String getNextKey() {
if (current >= Integer.MAX_VALUE) {
throw new RuntimeException("out of keys");
}
return keyPrefix + align(current++);
}
public String getValue(String key) {
int keyId = Integer.parseInt(key.replaceAll(keyPrefix, ""));
return valuePrefix + keyId;
}
}
// convert String to byte[]
private static byte[] toByteArray(String s)
throws UnsupportedEncodingException {
return s.getBytes("utf8");
}
}