/* * Minha.pt: middleware testing platform. * Copyright (c) 2011-2014, Universidade do Minho. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package pt.minha.calibration; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; import org.apache.commons.math3.stat.regression.SimpleRegression; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pt.minha.api.Entry; import pt.minha.api.sim.Calibration; import pt.minha.api.sim.Linear; import pt.minha.api.sim.Simulation; import pt.minha.calibration.AbstractBenchmark.Result; /** * Calibration tool. This tool runs a micro-benchmark and computes * configuration parameters that calibrate the simulator to mimic * the performance of the real hardware. */ public class Calibrator implements Closeable { private static Logger logger = LoggerFactory.getLogger("pt.minha.calibration"); private Socket socket; private ObjectOutputStream oos; private ObjectInputStream ois; public Calibrator(String server) throws UnknownHostException, IOException { logger.info("client: connecting to {}", server); socket = new Socket(server, 12345); oos = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream())); oos.flush(); ois = new ObjectInputStream(new BufferedInputStream(socket.getInputStream())); } private Result runReal(Map<String,Object> p) throws Throwable { // Reality logger.info("real: sending {}", p); oos.writeObject(p); oos.flush(); Thread.sleep(1000); logger.info("real: running {}", p); Benchmark next = (Benchmark) Class.forName((String) p.get("bench")).newInstance(); next.setParameters(p); Result rcli = (Result) next.client(); logger.info("real: running {} done", next); Object rsrv = ois.readObject(); logger.info("real server: {}", rsrv); logger.info("real client: {}", rcli); return rcli; } private Result runSimulated(Map<String,Object> p, Properties props) throws Throwable { Benchmark next = (Benchmark) Class.forName((String) p.get("bench")).newInstance(); next.setParameters(p); Simulation world = new Simulation(); Calibration c = world.getCalibration(); c.reset(); c.load(props); Entry<Benchmark>[] e = world.createEntries(2, Benchmark.class, (String) p.get("bench")); logger.info("simulation: loading {}", next); InetSocketAddress srv = new InetSocketAddress(e[1].getProcess().getHost().getAddress(), 20000); p.put("server", srv); e[0].call().setParameters(p); e[1].call().setParameters(p); logger.info("simulation: running {}", next); e[1].queue().server(); e[0].at(1, TimeUnit.SECONDS).queue().client(); world.runAll(e); Object scli = e[0].getResult(); Object ssrv = e[1].getResult(); world.close(); logger.info("simulation server: got {}", ssrv); logger.info("simulation client: got {}", scli); return (Result) scli; } public void close() throws IOException { socket.close(); } private static void runServer() throws Exception { // Reality server ServerSocket ss = new ServerSocket(12345); logger.info("server: started at {}", ss.getLocalSocketAddress()); while(true) { Socket s = ss.accept(); logger.info("server: accepted {}", s.getRemoteSocketAddress()); try { ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(s.getOutputStream())); oos.flush(); ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(s.getInputStream())); while(!s.isClosed()) { Map<String,Object> p = (Map<String,Object>) ois.readObject(); Benchmark next = (Benchmark) Class.forName((String) p.get("bench")).newInstance(); next.setParameters(p); logger.info("server: running {}", p); Object result = next.server(); logger.info("server: running {} done", p); oos.writeObject(result); oos.flush(); } logger.info("server: disconnected {}", s.getRemoteSocketAddress()); } catch(IOException ioe) { logger.info("server: disconnected {} on {}", s.getRemoteSocketAddress(), ioe); } } } public static void main(String[] args) throws Throwable { if (args.length != 1) { logger.error("missing command line argument (--server | <servername>)"); } else if (args[0].equals("--server")) { runServer(); } else { Calibrator calib = new Calibrator(args[0]); Properties props = new Properties(); SimpleRegression cpuoh = new SimpleRegression(true); for(int i: new int[]{100, 1000, 10000, 20000}) { Map<String,Object> p = new HashMap<String, Object>(); p.put("bench", CPUBenchmark.class.getName()); p.put("samples", 50000); p.put("payload", i); Result r = calib.runReal(p); Result s = calib.runSimulated(p, props); cpuoh.addData(s.meanCPU, r.meanCPU); } props.setProperty("cpuScaling", new Linear(cpuoh.getIntercept(), cpuoh.getSlope()).toString()); // Run double max = Double.MIN_VALUE; SimpleRegression netCPU = new SimpleRegression(true); SimpleRegression netCPU_s = new SimpleRegression(true); for(int i: new int[]{1, 100, 1000, 4000, 8000, 16000}) { Map<String,Object> p = new HashMap<String, Object>(); p.put("bench", TCPOverheadBenchmark.class.getName()); p.put("server", new InetSocketAddress(args[0], 20000)); p.put("samples", 5000); p.put("payload", i); Result r = calib.runReal(p); netCPU.addData(i, r.meanCPU); Result s = calib.runSimulated(p, props); netCPU_s.addData(i, s.meanCPU); double bw = 8*i*1e9d/r.meanLatency; if (bw > max) max = bw; } props.setProperty("networkBandwidth", Long.toString((long)max)); props.setProperty("tcpOverhead", new Linear( (netCPU.getIntercept()-netCPU_s.getIntercept())/2, (netCPU.getSlope()-netCPU_s.getSlope())/2).toString()); SimpleRegression udpCPU = new SimpleRegression(true); SimpleRegression udpCPU_s = new SimpleRegression(true); for(int i: new int[]{1, 100, 1000, 4000, 8000, 16000}) { Map<String,Object> p = new HashMap<String, Object>(); p.put("bench", UDPOverheadBenchmark.class.getName()); p.put("server", new InetSocketAddress(args[0], 20000)); p.put("samples", 5000); p.put("payload", i); Result r = calib.runReal(p); udpCPU.addData(i, r.meanCPU); Result s = calib.runSimulated(p, props); udpCPU_s.addData(i, s.meanCPU); } props.setProperty("udpOverhead", new Linear( (udpCPU.getIntercept()-udpCPU_s.getIntercept())/2, (udpCPU.getSlope()-udpCPU_s.getSlope())/2).toString()); SimpleRegression rtt = new SimpleRegression(true); SimpleRegression rtt_s = new SimpleRegression(true); for(int i: new int[]{1, 100, 1000, 4000, 8000, 16000}) { Map<String,Object> p = new HashMap<String, Object>(); p.put("bench", TCPLatencyBenchmark.class.getName()); p.put("server", new InetSocketAddress(args[0], 20000)); p.put("samples", 5000); p.put("payload", i); Result r = calib.runReal(p); rtt.addData(i, r.meanLatency); Result s = calib.runSimulated(p, props); rtt_s.addData(i, s.meanLatency); } props.setProperty("networkLatency", new Linear( (rtt.getIntercept()-rtt_s.getIntercept())/2, (rtt.getSlope()-rtt_s.getSlope())/2).toString()); calib.close(); for(String key: props.stringPropertyNames()) logger.info("result: {}={}", key, props.getProperty(key)); // Write results FileOutputStream file = new FileOutputStream("calibration.properties"); props.store(file, "Generated calibration properties"); file.close(); } } }