content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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 org.apache.activemq.store.kahadb.disk.util;
  
  import java.io.File;
  import java.io.RandomAccessFile;
  import java.util.ArrayList;
  import java.util.Arrays;
  
  import org.apache.activemq.util.RecoverableRandomAccessFile;
  
  /**
   * This class is used to get a benchmark the raw disk performance.
   */
  public class DiskBenchmark {
  
      private static final boolean SKIP_METADATA_UPDATE =
          Boolean.getBoolean("org.apache.activemq.file.skipMetadataUpdate");
  
      boolean verbose;
      // reads and writes work with 4k of data at a time.
      int bs = 1024 * 4;
      // Work with 100 meg file.
      long size = 1024 * 1024 * 500;
      long sampleInterval = 10 * 1000;
  
      public static void main(String[] args) {
  
          DiskBenchmark benchmark = new DiskBenchmark();
          args = CommandLineSupport.setOptions(benchmark, args);
          ArrayList<String> files = new ArrayList<String>();
          if (args.length == 0) {
              files.add("disk-benchmark.dat");
          } else {
              files.addAll(Arrays.asList(args));
          }
  
          for (String f : files) {
              try {
                  File file = new File(f);
                  if (file.exists()) {
                      System.out.println("File " + file + " already exists, will not benchmark.");
                  } else {
                      System.out.println("Benchmarking: " + file.getCanonicalPath());
                      Report report = benchmark.benchmark(file);
                      file.delete();
                      System.out.println(report.toString());
                  }
              } catch (Throwable e) {
                  if (benchmark.verbose) {
                      System.out.println("ERROR:");
                      e.printStackTrace(System.out);
                  } else {
                      System.out.println("ERROR: " + e);
                  }
              }
          }
  
      }
  
      public static class Report {
  
          public int size;
  
          public int writes;
          public long writeDuration;
  
          public int syncWrites;
          public long syncWriteDuration;
  
          public int reads;
          public long readDuration;
  
          @Override
          public String toString() {
              return "Writes: \n" + "  " + writes + " writes of size " + size + " written in " + (writeDuration / 1000.0) + " seconds.\n" + "  " + getWriteRate()
                  + " writes/second.\n" + "  " + getWriteSizeRate() + " megs/second.\n" + "\n" + "Sync Writes: \n" + "  " + syncWrites + " writes of size "
                  + size + " written in " + (syncWriteDuration / 1000.0) + " seconds.\n" + "  " + getSyncWriteRate() + " writes/second.\n" + "  "
                  + getSyncWriteSizeRate() + " megs/second.\n" + "\n" + "Reads: \n" + "  " + reads + " reads of size " + size + " read in "
                  + (readDuration / 1000.0) + " seconds.\n" + "  " + getReadRate() + " writes/second.\n" + "  " + getReadSizeRate() + " megs/second.\n" + "\n"
                  + "";
          }
  
          private float getWriteSizeRate() {
              float rc = writes;
              rc *= size;
              rc /= (1024 * 1024); // put it in megs
              rc /= (writeDuration / 1000.0); // get rate.
              return rc;
          }
  
          private float getWriteRate() {
              float rc = writes;
              rc /= (writeDuration / 1000.0); // get rate.
              return rc;
          }
  
          private float getSyncWriteSizeRate() {
              float rc = syncWrites;
              rc *= size;
              rc /= (1024 * 1024); // put it in megs
              rc /= (syncWriteDuration / 1000.0); // get rate.
              return rc;
          }
  
          private float getSyncWriteRate() {
              float rc = syncWrites;
              rc /= (syncWriteDuration / 1000.0); // get rate.
              return rc;
          }
  
          private float getReadSizeRate() {
              float rc = reads;
              rc *= size;
              rc /= (1024 * 1024); // put it in megs
              rc /= (readDuration / 1000.0); // get rate.
              return rc;
          }
  
          private float getReadRate() {
              float rc = reads;
              rc /= (readDuration / 1000.0); // get rate.
              return rc;
          }
  
          public int getSize() {
              return size;
          }
  
          public void setSize(int size) {
              this.size = size;
          }
  
          public int getWrites() {
              return writes;
          }
  
          public void setWrites(int writes) {
              this.writes = writes;
          }
  
          public long getWriteDuration() {
              return writeDuration;
          }
  
          public void setWriteDuration(long writeDuration) {
              this.writeDuration = writeDuration;
          }
  
          public int getSyncWrites() {
              return syncWrites;
          }
  
          public void setSyncWrites(int syncWrites) {
              this.syncWrites = syncWrites;
          }
  
          public long getSyncWriteDuration() {
              return syncWriteDuration;
          }
  
          public void setSyncWriteDuration(long syncWriteDuration) {
              this.syncWriteDuration = syncWriteDuration;
          }
  
          public int getReads() {
              return reads;
          }
  
          public void setReads(int reads) {
              this.reads = reads;
          }
  
          public long getReadDuration() {
              return readDuration;
          }
  
          public void setReadDuration(long readDuration) {
              this.readDuration = readDuration;
          }
      }
  
      public Report benchmark(File file) throws Exception {
          Report rc = new Report();
  
          // Initialize the block we will be writing to disk.
          byte[] data = new byte[bs];
          for (int i = 0; i < data.length; i++) {
              data[i] = (byte) ('a' + (i % 26));
          }
          rc.size = data.length;
  
          long start;
          long now;
          int ioCount;
  
          try(RecoverableRandomAccessFile raf = new RecoverableRandomAccessFile(file, "rw")) {
              preallocateDataFile(raf, file.getParentFile());
              start = System.currentTimeMillis();
              now = System.currentTimeMillis();
              ioCount = 0;
  
              // Figure out how many writes we can do in the sample interval.
              while (true) {
                  if ((now - start) > sampleInterval) {
                      break;
                  }
                  raf.seek(0);
                  for (long i = 0; i + data.length < size; i += data.length) {
                      raf.write(data);
                      ioCount++;
                      now = System.currentTimeMillis();
                      if ((now - start) > sampleInterval) {
                          break;
                      }
                  }
                  // Sync to disk so that the we actually write the data to disk..
                  // otherwise OS buffering might not really do the write.
                  raf.getChannel().force(!SKIP_METADATA_UPDATE);
              }
              raf.getChannel().force(!SKIP_METADATA_UPDATE);
          }
          now = System.currentTimeMillis();
  
          rc.size = data.length;
          rc.writes = ioCount;
          rc.writeDuration = (now - start);
  
          try(RecoverableRandomAccessFile raf = new RecoverableRandomAccessFile(file, "rw")) {
              start = System.currentTimeMillis();
              now = System.currentTimeMillis();
              ioCount = 0;
              while (true) {
                  if ((now - start) > sampleInterval) {
                      break;
                  }
                  for (long i = 0; i + data.length < size; i += data.length) {
                      raf.seek(i);
                      raf.write(data);
                      raf.getChannel().force(!SKIP_METADATA_UPDATE);
                      ioCount++;
                      now = System.currentTimeMillis();
                      if ((now - start) > sampleInterval) {
                          break;
                      }
                  }
              }
          }
          now = System.currentTimeMillis();
          rc.syncWrites = ioCount;
          rc.syncWriteDuration = (now - start);
  
          try(RecoverableRandomAccessFile raf = new RecoverableRandomAccessFile(file, "rw")) {
              start = System.currentTimeMillis();
              now = System.currentTimeMillis();
              ioCount = 0;
              while (true) {
                  if ((now - start) > sampleInterval) {
                      break;
                  }
                  raf.seek(0);
                  for (long i = 0; i + data.length < size; i += data.length) {
                      raf.seek(i);
                      raf.readFully(data);
                      ioCount++;
                      now = System.currentTimeMillis();
                      if ((now - start) > sampleInterval) {
                          break;
                      }
                  }
              }
          }
  
          rc.reads = ioCount;
          rc.readDuration = (now - start);
          return rc;
      }
  
      private void preallocateDataFile(RecoverableRandomAccessFile raf, File location) throws Exception {
          File tmpFile;
          if (location != null && location.isDirectory()) {
              tmpFile = new File(location, "template.dat");
          }else {
              tmpFile = new File("template.dat");
          }
          if (tmpFile.exists()) {
              tmpFile.delete();
          }
          try (RandomAccessFile templateFile = new RandomAccessFile(tmpFile, "rw");) {
              templateFile.setLength(size);
              templateFile.getChannel().force(true);
              templateFile.getChannel().transferTo(0, size, raf.getChannel());
          }
          tmpFile.delete();
      }
  
      public boolean isVerbose() {
          return verbose;
      }
  
      public void setVerbose(boolean verbose) {
          this.verbose = verbose;
      }
  
      public int getBs() {
          return bs;
      }
  
      public void setBs(int bs) {
          this.bs = bs;
      }
  
      public long getSize() {
          return size;
      }
  
      public void setSize(long size) {
          this.size = size;
      }
  
      public long getSampleInterval() {
          return sampleInterval;
      }
  
      public void setSampleInterval(long sampleInterval) {
          this.sampleInterval = sampleInterval;
      }
  }
  