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