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