Permalink
Browse files

Voldemort Performance Tool (Runs local & remote test)

- Run using bin/voldemort-performance-tool.sh
- Has a warmup phase to insert records (--record-count)
- Various record selection distributions
- Can fix client throughput to measure latency under certain load
  • Loading branch information...
1 parent 448cad5 commit 8462031d2d8b676b27f533f8f7de5631c8eb70dd @rsumbaly rsumbaly committed Jun 3, 2010
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+#
+# Copyright 2008-2009 LinkedIn, Inc
+#
+# 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.
+#
+
+bin_dir=$(dirname $0)
+
+${bin_dir}/run-class.sh voldemort.performance.benchmark.Benchmark $@

Large diffs are not rendered by default.

Oops, something went wrong.
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010 Yahoo! Inc. 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. See accompanying LICENSE file.
+ */
+
+package voldemort.performance.benchmark;
+
+import java.util.Random;
+
+public class BenchmarkUtils {
+
+ private static Random random = new Random();
+
+ public static String ASCIIString(int length) {
+ int interval = '~' - ' ' + 1;
+
+ byte[] buf = new byte[length];
+ random.nextBytes(buf);
+ for(int i = 0; i < length; i++) {
+ if(buf[i] < 0) {
+ buf[i] = (byte) ((-buf[i] % interval) + ' ');
+ } else {
+ buf[i] = (byte) ((buf[i] % interval) + ' ');
+ }
+ }
+ return new String(buf);
+ }
+
+ public static int hash(int val) {
+ return FNVhash32(val);
+ }
+
+ public static final int FNV_offset_basis_32 = 0x811c9dc5;
+ public static final int FNV_prime_32 = 16777619;
+
+ public static int FNVhash32(int val) {
+ int hashval = FNV_offset_basis_32;
+
+ for(int i = 0; i < 4; i++) {
+ int octet = val & 0x00ff;
+ val = val >> 8;
+
+ hashval = hashval ^ octet;
+ hashval = hashval * FNV_prime_32;
+ }
+ return Math.abs(hashval);
+ }
+
+ public static final long FNV_offset_basis_64 = 0xCBF29CE484222325L;
+ public static final long FNV_prime_64 = 1099511628211L;
+
+ public static long FNVhash64(long val) {
+ long hashval = FNV_offset_basis_64;
+
+ for(int i = 0; i < 8; i++) {
+ long octet = val & 0x00ff;
+ val = val >> 8;
+
+ hashval = hashval ^ octet;
+ hashval = hashval * FNV_prime_64;
+ }
+ return Math.abs(hashval);
+ }
+}
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2010 LinkedIn, Inc
+ *
+ * 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.
+ */
+package voldemort.performance.benchmark;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+
+class Results {
+
+ public Results(int ops, int minL, int maxL, long totL, int medL, int q95, int q99) {
+ operations = ops;
+ minLatency = minL;
+ maxLatency = maxL;
+ totalLatency = totL;
+ medianLatency = medL;
+ q99Latency = q99;
+ q95Latency = q95;
+ }
+
+ public int operations = -1;
+ public int minLatency = -1;
+ public int maxLatency = -1;
+ public long totalLatency = -1;
+ public int medianLatency = -1;
+ public int q99Latency = -1;
+ public int q95Latency = -1;
+
+ @Override
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("OPERATIONS = " + operations);
+ buffer.append("MINLATENCY = " + minLatency);
+ buffer.append("MAXLATENCY = " + maxLatency);
+ buffer.append("MEDIANLATENCY = " + medianLatency);
+ buffer.append("Q95LATENCY = " + q95Latency);
+ buffer.append("Q99LATENCY = " + q99Latency);
+
+ return buffer.toString();
+
+ }
+}
+
+public class Measurement {
+
+ private String _name;
+
+ public String getName() {
+ return _name;
+ }
+
+ private int _buckets;
+ private int[] histogram;
+ private int histogramOverflow;
+ private int windowOperations;
+ private long windowTotalLatency;
+ private int operations = 0;
+ private int minLatency = -1;
+ private int maxLatency = -1;
+ private long totalLatency = 0;
+ private HashMap<Integer, int[]> returnCodes;
+ private boolean summaryOnly = false;
+
+ public Measurement(String name, boolean summaryOnly) {
+ this._name = name;
+ this._buckets = 1000;
+ this.histogram = new int[_buckets];
+ this.histogramOverflow = 0;
+ this.operations = 0;
+ this.totalLatency = 0;
+ this.windowOperations = 0;
+ this.windowTotalLatency = 0;
+ this.minLatency = -1;
+ this.maxLatency = -1;
+ this.returnCodes = new HashMap<Integer, int[]>();
+ this.summaryOnly = summaryOnly;
+ }
+
+ public synchronized void reportReturnCode(int code) {
+ Integer Icode = code;
+ if(!returnCodes.containsKey(Icode)) {
+ int[] val = new int[1];
+ val[0] = 0;
+ returnCodes.put(Icode, val);
+ }
+ returnCodes.get(Icode)[0]++;
+ }
+
+ public synchronized void measure(int latency) {
+ if(latency >= _buckets) {
+ histogramOverflow++;
+ } else {
+ histogram[latency]++;
+ }
+ operations++;
+ totalLatency += latency;
+ windowOperations++;
+ windowTotalLatency += latency;
+
+ if((minLatency < 0) || (latency < minLatency)) {
+ minLatency = latency;
+ }
+
+ if((maxLatency < 0) || (latency > maxLatency)) {
+ maxLatency = latency;
+ }
+ }
+
+ public Results generateResults() {
+ int median = 0, q95 = 0, q99 = 0;
+ int opcounter = 0;
+ boolean done95th = false, done50th = false;
+ for(int i = 0; i < _buckets; i++) {
+ opcounter += histogram[i];
+ double currentQuartile = ((double) opcounter) / ((double) operations);
+ if(!done50th && currentQuartile >= 0.50) {
+ median = i;
+ done50th = true;
+ }
+ if(!done95th && currentQuartile >= 0.95) {
+ q95 = i;
+ done95th = true;
+ }
+ if(currentQuartile >= 0.99) {
+ q99 = i;
+ break;
+ }
+ }
+ return new Results(operations, minLatency, maxLatency, totalLatency, median, q95, q99);
+ }
+
+ public void printReport(PrintStream out) {
+
+ Results result = generateResults();
+ out.println("[" + getName() + "]\tOperations: " + operations);
+ out.println("[" + getName() + "]\tAverage(ms): "
+ + (((double) totalLatency) / ((double) operations)));
+ out.println("[" + getName() + "]\tMin(ms): " + minLatency);
+ out.println("[" + getName() + "]\tMax(ms): " + maxLatency);
+ out.println("[" + getName() + "]\tMedian(ms): " + result.medianLatency);
+ out.println("[" + getName() + "]\t95th(ms): " + result.q95Latency);
+ out.println("[" + getName() + "]\t99th(ms): " + result.q99Latency);
+
+ if(!this.summaryOnly) {
+ for(Integer I: returnCodes.keySet()) {
+ int[] val = returnCodes.get(I);
+ out.println("[" + getName() + "]\tReturn: " + I + "\t" + val[0]);
+ }
+
+ for(int i = 0; i < _buckets; i++) {
+ out.println("[" + getName() + "]: " + i + "\t" + histogram[i]);
+ }
+ out.println("[" + getName() + "]: >" + _buckets + "\t" + histogramOverflow);
+ }
+ }
+}
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2010 Yahoo! Inc. 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. See accompanying LICENSE file.
+ */
+
+package voldemort.performance.benchmark;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+import voldemort.utils.Props;
+
+public class Metrics {
+
+ private static Metrics singleton = null;
+ private static Props metricProps = null;
+
+ private ConcurrentHashMap<String, Measurement> data;
+ private boolean summaryOnly = false;
+
+ public static void setProperties(Props props) {
+ metricProps = props;
+ }
+
+ public synchronized static Metrics getInstance() {
+ if(singleton == null) {
+ singleton = new Metrics();
+ }
+ return singleton;
+ }
+
+ private Metrics() {
+ this.data = new ConcurrentHashMap<String, Measurement>();
+
+ String metricType = Metrics.metricProps.getString(Benchmark.METRIC_TYPE,
+ Benchmark.SUMMARY_METRIC_TYPE);
+ if(metricType.compareTo(Benchmark.SUMMARY_METRIC_TYPE) == 0) {
+ this.summaryOnly = true;
+ } else {
+ this.summaryOnly = false;
+ }
+ }
+
+ private Measurement constructMeasurement(String name) {
+ return new Measurement(name, this.summaryOnly);
+ }
+
+ public synchronized void measure(String operation, int latency) {
+ if(!data.containsKey(operation)) {
+ synchronized(this) {
+ if(!data.containsKey(operation)) {
+ data.put(operation, constructMeasurement(operation));
+ }
+ }
+ }
+ try {
+ data.get(operation).measure(latency);
+ } catch(java.lang.ArrayIndexOutOfBoundsException e) {
+ // Do nothing
+ }
+ }
+
+ public void reportReturnCode(String operation, int code) {
+ if(!data.containsKey(operation)) {
+ synchronized(this) {
+ if(!data.containsKey(operation)) {
+ data.put(operation, constructMeasurement(operation));
+ }
+ }
+ }
+ data.get(operation).reportReturnCode(code);
+ }
+
+ public void reset() {
+ data.clear();
+ }
+
+ public void printReport(PrintStream out) {
+ for(Measurement m: data.values()) {
+ m.printReport(out);
+ }
+ }
+
+ public HashMap<String, Results> getResults() {
+ HashMap<String, Results> returnMap = new HashMap<String, Results>();
+ for(Measurement m: data.values()) {
+ returnMap.put(m.getName(), m.generateResults());
+ }
+ return returnMap;
+ }
+
+}
Oops, something went wrong.

0 comments on commit 8462031

Please sign in to comment.