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     */
017    package org.apache.kahadb.util;
018    
019    import java.io.File;
020    import java.io.FileNotFoundException;
021    import java.io.IOException;
022    import java.io.RandomAccessFile;
023    import java.util.ArrayList;
024    import java.util.Arrays;
025    
026    /**
027     * This class is used to get a benchmark the raw disk performance.
028     */
029    public 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    }