001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.store.kahadb.disk.util; 018 019import org.apache.activemq.util.RecoverableRandomAccessFile; 020 021import java.io.File; 022import java.io.IOException; 023import java.io.RandomAccessFile; 024import java.util.ArrayList; 025import java.util.Arrays; 026 027/** 028 * This class is used to get a benchmark the raw disk performance. 029 */ 030public class DiskBenchmark { 031 032 private static final boolean SKIP_METADATA_UPDATE = 033 Boolean.getBoolean("org.apache.activemq.file.skipMetadataUpdate"); 034 035 boolean verbose; 036 // reads and writes work with 4k of data at a time. 037 int bs = 1024 * 4; 038 // Work with 100 meg file. 039 long size = 1024 * 1024 * 500; 040 long sampleInterval = 10 * 1000; 041 042 public static void main(String[] args) { 043 044 DiskBenchmark benchmark = new DiskBenchmark(); 045 args = CommandLineSupport.setOptions(benchmark, args); 046 ArrayList<String> files = new ArrayList<String>(); 047 if (args.length == 0) { 048 files.add("disk-benchmark.dat"); 049 } else { 050 files.addAll(Arrays.asList(args)); 051 } 052 053 for (String f : files) { 054 try { 055 File file = new File(f); 056 if (file.exists()) { 057 System.out.println("File " + file + " already exists, will not benchmark."); 058 } else { 059 System.out.println("Benchmarking: " + file.getCanonicalPath()); 060 Report report = benchmark.benchmark(file); 061 file.delete(); 062 System.out.println(report.toString()); 063 } 064 } catch (Throwable e) { 065 if (benchmark.verbose) { 066 System.out.println("ERROR:"); 067 e.printStackTrace(System.out); 068 } else { 069 System.out.println("ERROR: " + e); 070 } 071 } 072 } 073 074 } 075 076 public static class Report { 077 078 public int size; 079 080 public int writes; 081 public long writeDuration; 082 083 public int syncWrites; 084 public long syncWriteDuration; 085 086 public int reads; 087 public long readDuration; 088 089 @Override 090 public String toString() { 091 return "Writes: \n" + " " + writes + " writes of size " + size + " written in " + (writeDuration / 1000.0) + " seconds.\n" + " " + getWriteRate() 092 + " writes/second.\n" + " " + getWriteSizeRate() + " megs/second.\n" + "\n" + "Sync Writes: \n" + " " + syncWrites + " writes of size " 093 + size + " written in " + (syncWriteDuration / 1000.0) + " seconds.\n" + " " + getSyncWriteRate() + " writes/second.\n" + " " 094 + getSyncWriteSizeRate() + " megs/second.\n" + "\n" + "Reads: \n" + " " + reads + " reads of size " + size + " read in " 095 + (readDuration / 1000.0) + " seconds.\n" + " " + getReadRate() + " writes/second.\n" + " " + getReadSizeRate() + " megs/second.\n" + "\n" 096 + ""; 097 } 098 099 private float getWriteSizeRate() { 100 float rc = writes; 101 rc *= size; 102 rc /= (1024 * 1024); // put it in megs 103 rc /= (writeDuration / 1000.0); // get rate. 104 return rc; 105 } 106 107 private float getWriteRate() { 108 float rc = writes; 109 rc /= (writeDuration / 1000.0); // get rate. 110 return rc; 111 } 112 113 private float getSyncWriteSizeRate() { 114 float rc = syncWrites; 115 rc *= size; 116 rc /= (1024 * 1024); // put it in megs 117 rc /= (syncWriteDuration / 1000.0); // get rate. 118 return rc; 119 } 120 121 private float getSyncWriteRate() { 122 float rc = syncWrites; 123 rc /= (syncWriteDuration / 1000.0); // get rate. 124 return rc; 125 } 126 127 private float getReadSizeRate() { 128 float rc = reads; 129 rc *= size; 130 rc /= (1024 * 1024); // put it in megs 131 rc /= (readDuration / 1000.0); // get rate. 132 return rc; 133 } 134 135 private float getReadRate() { 136 float rc = reads; 137 rc /= (readDuration / 1000.0); // get rate. 138 return rc; 139 } 140 141 public int getSize() { 142 return size; 143 } 144 145 public void setSize(int size) { 146 this.size = size; 147 } 148 149 public int getWrites() { 150 return writes; 151 } 152 153 public void setWrites(int writes) { 154 this.writes = writes; 155 } 156 157 public long getWriteDuration() { 158 return writeDuration; 159 } 160 161 public void setWriteDuration(long writeDuration) { 162 this.writeDuration = writeDuration; 163 } 164 165 public int getSyncWrites() { 166 return syncWrites; 167 } 168 169 public void setSyncWrites(int syncWrites) { 170 this.syncWrites = syncWrites; 171 } 172 173 public long getSyncWriteDuration() { 174 return syncWriteDuration; 175 } 176 177 public void setSyncWriteDuration(long syncWriteDuration) { 178 this.syncWriteDuration = syncWriteDuration; 179 } 180 181 public int getReads() { 182 return reads; 183 } 184 185 public void setReads(int reads) { 186 this.reads = reads; 187 } 188 189 public long getReadDuration() { 190 return readDuration; 191 } 192 193 public void setReadDuration(long readDuration) { 194 this.readDuration = readDuration; 195 } 196 } 197 198 public Report benchmark(File file) throws Exception { 199 Report rc = new Report(); 200 201 // Initialize the block we will be writing to disk. 202 byte[] data = new byte[bs]; 203 for (int i = 0; i < data.length; i++) { 204 data[i] = (byte) ('a' + (i % 26)); 205 } 206 207 rc.size = data.length; 208 RecoverableRandomAccessFile raf = new RecoverableRandomAccessFile(file, "rw"); 209 preallocateDataFile(raf, file.getParentFile()); 210 211 // Figure out how many writes we can do in the sample interval. 212 long start = System.currentTimeMillis(); 213 long now = System.currentTimeMillis(); 214 int ioCount = 0; 215 while (true) { 216 if ((now - start) > sampleInterval) { 217 break; 218 } 219 raf.seek(0); 220 for (long i = 0; i + data.length < size; i += data.length) { 221 raf.write(data); 222 ioCount++; 223 now = System.currentTimeMillis(); 224 if ((now - start) > sampleInterval) { 225 break; 226 } 227 } 228 // Sync to disk so that the we actually write the data to disk.. 229 // otherwise OS buffering might not really do the write. 230 raf.getChannel().force(!SKIP_METADATA_UPDATE); 231 } 232 raf.getChannel().force(!SKIP_METADATA_UPDATE); 233 raf.close(); 234 now = System.currentTimeMillis(); 235 236 rc.size = data.length; 237 rc.writes = ioCount; 238 rc.writeDuration = (now - start); 239 240 raf = new RecoverableRandomAccessFile(file, "rw"); 241 start = System.currentTimeMillis(); 242 now = System.currentTimeMillis(); 243 ioCount = 0; 244 while (true) { 245 if ((now - start) > sampleInterval) { 246 break; 247 } 248 for (long i = 0; i + data.length < size; i += data.length) { 249 raf.seek(i); 250 raf.write(data); 251 raf.getChannel().force(!SKIP_METADATA_UPDATE); 252 ioCount++; 253 now = System.currentTimeMillis(); 254 if ((now - start) > sampleInterval) { 255 break; 256 } 257 } 258 } 259 raf.close(); 260 now = System.currentTimeMillis(); 261 rc.syncWrites = ioCount; 262 rc.syncWriteDuration = (now - start); 263 264 raf = new RecoverableRandomAccessFile(file, "rw"); 265 start = System.currentTimeMillis(); 266 now = System.currentTimeMillis(); 267 ioCount = 0; 268 while (true) { 269 if ((now - start) > sampleInterval) { 270 break; 271 } 272 raf.seek(0); 273 for (long i = 0; i + data.length < size; i += data.length) { 274 raf.seek(i); 275 raf.readFully(data); 276 ioCount++; 277 now = System.currentTimeMillis(); 278 if ((now - start) > sampleInterval) { 279 break; 280 } 281 } 282 } 283 raf.close(); 284 285 rc.reads = ioCount; 286 rc.readDuration = (now - start); 287 return rc; 288 } 289 290 private void preallocateDataFile(RecoverableRandomAccessFile raf, File location) throws Exception { 291 File tmpFile; 292 if (location != null && location.isDirectory()) { 293 tmpFile = new File(location, "template.dat"); 294 }else { 295 tmpFile = new File("template.dat"); 296 } 297 if (tmpFile.exists()) { 298 tmpFile.delete(); 299 } 300 RandomAccessFile templateFile = new RandomAccessFile(tmpFile, "rw"); 301 templateFile.setLength(size); 302 templateFile.getChannel().force(true); 303 templateFile.getChannel().transferTo(0, size, raf.getChannel()); 304 templateFile.close(); 305 tmpFile.delete(); 306 } 307 308 public boolean isVerbose() { 309 return verbose; 310 } 311 312 public void setVerbose(boolean verbose) { 313 this.verbose = verbose; 314 } 315 316 public int getBs() { 317 return bs; 318 } 319 320 public void setBs(int bs) { 321 this.bs = bs; 322 } 323 324 public long getSize() { 325 return size; 326 } 327 328 public void setSize(long size) { 329 this.size = size; 330 } 331 332 public long getSampleInterval() { 333 return sampleInterval; 334 } 335 336 public void setSampleInterval(long sampleInterval) { 337 this.sampleInterval = sampleInterval; 338 } 339}