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