| /** |
| * 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; |
| } |
| } |