/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.indexStructures;
import xxl.core.io.raw.NativeRawAccess;
import xxl.core.io.raw.RAFRawAccess;
import xxl.core.io.raw.RawAccess;
import xxl.core.io.raw.RawAccessUtils;
import xxl.core.io.raw.StatisticsRawAccess;
import xxl.core.util.concurrency.AsynchronousChannel;
import xxl.core.util.random.DiscreteRandomWrapper;
import xxl.core.util.reflect.TestFramework;
import xxl.core.util.timers.Timer;
import xxl.core.util.timers.TimerUtils;
/**
* Perform some tests with raw accesses.
*/
public class RawAccessTest {
/** Description for variable below. */
public static final String fn1Description = "The name of the first raw device to be tested";
/** The name of the first raw device to be tested. */
// public static String fn1 = "D:/test.txt";
// public static String fn1 = "\\\\.\\A:";
// public static String fn1 = "\\\\.\\D:";
// public static String fn1 = "\\\\.\\PhysicalDrive0";
public static String fn1 = "\\\\.\\PhysicalDrive2";
// public static String fn1 = "\\\\.\\C:";
// public static String fn1 = "\\\\.\\jd.txt";
// public static String fn1 = "D:/test2.txt";
// public static String fn1 = "\\\\.\\H:";
/** Description for variable below. */
public static final String fn2Description = "The name of the second raw device to be tested";
/** The name of the second raw device to be tested. */
public static String fn2 = "D:/test2.txt";
/** Description for variable below. */
public static final String testNrDescription =
"The number of the test that should be performed. "+
"-1: No test performed, "+
"0: Read sector 0 repeatedly, "+
"1: Read sequentially from a RawAccess changing the sectorSize for the operations, "+
"2: Testing seeks of a predefined length, "+
"3: Testing seeks with random length, "+
"4: Copying a RawAccess.";
/**
* Look inside Variable testNrDescription above! Default value must be -1,
* because of MainMaker!
*/
// public static int testNr=-1;
public static int testNr=5;
/** */
public static final int testNrMin=-1;
/** */
public static final int testNrMax=4;
/** Description for variable below. */
public static final String numberOfChunksDescription = "The number of sector that should be manipulated during the test.";
/** The number of sector that should be manipulated during the test. */
// public static int numberOfChunks = 80*2*18;
// public static int numberOfChunks = 36;
public static int numberOfChunks = 1000;
/** A boolean flag determining whether the raw access should be performed via native methods (JNI and C) or a random-access file. */
public static boolean useNative=true;
/** For TestFramework */ public static final String useNativeDescription = "A boolean flag determining whether the raw access should be performed via native methods (JNI and C) or a random-access file.";
/** A boolean flag deciding whether a synchronization call after each read/write operation is performed. */
public static boolean useSync=false;
/** For TestFramework */ public static final String useSyncDescription = "A boolean flag deciding whether a synchronization call after each read/write operation is performed.";
/** A boolean flag deciding whether statistical informations should be calculated during the test. */
public static boolean makeStatistic=false;
/** For TestFramework */ public static final String makeStatisticDescription = "A boolean flag deciding whether statistical informations should be calculated during the test.";
/** For testNr==1: The size of a physical sector. */
public static int physicalSectorSize = 512;
/** For TestFramework */ public static final String physicalSectorSizeDescription = "For testNr==1: The size of a physical sector.";
/** For testNr==1: The initial sector size for the test. */
public static int startSectorSize = 10*physicalSectorSize;
/** For TestFramework */ public static final String startSectorSizeDescription = "For testNr==1: The initial sector size for the test.";
/** For testNr==1: The final sector size for the test. */
public static int endSectorSize = 100*physicalSectorSize; // startSectorSize*100;
/** For TestFramework */ public static final String endSectorSizeDescription = "For testNr==1: The final sector size for the test.";
/** For testNr==1: The size by which the sector size is increased during the steps of the test. */
public static int stepSectorSize = 5*physicalSectorSize;
/** For TestFramework */ public static final String stepSectorSizeDescription = "For testNr==1: The size by which the sector size is increased during the steps of the test.";
/** For testNr==2: A boolean flag deciding whether the test starts seeking from position 0. */
public static boolean seekFrom0=true;
/** For TestFramework */ public static final String seekFrom0Description = "For testNr==2: A boolean flag deciding whether the test starts seeking from position 0.";
/** For testNr==2: The number of sectors skipped between two seek operations. */
public static int stepwide = 100000;
/** For TestFramework */ public static final String stepwideDescription = "For testNr==2: The number of sectors skipped between two seek operations.";
/** For testNr==3: A boolean flag determining whether Java's random number generator should be used or not. */
public static boolean randomJava=true;
/** For TestFramework */ public static final String randomJavaDescription = "For testNr==3: A boolean flag determining whether Java's random number generator should be used or not.";
/** For testNr==3: The number of seek operations that should be performed during the test. */
public static int numSeeks = 10000;
/** For TestFramework */ public static final String numSeeksDescription = "For testNr==3: The number of seek operations that should be performed during the test.";
/** Mode for the hard disk cache (cannot be guaranteed, depends on specific hard disk drive). Bit 0: Read Cache, Bit 1: Write Cache. */
public static int hardDiskCacheMode=0;
/** For TestFramework */ public static final String hardDiskCacheModeDescription = "Mode for the hard disk cache (cannot be guaranteed, depends on specific hard disk drive). Bit 0: Read Cache, Bit 1: Write Cache.";
/** For testNr==4: A boolean flag determining whether the destination file should be created before the copy operation will be performed. */
public static boolean construct=true;
/** For TestFramework */ public static final String constructDescription = "For testNr==4: A boolean flag determining whether the destination file should be created before the copy operation will be performed.";
/** For testNr==4: A boolean flag deciding whether the source and the destination file should be filled with data before the copy operation will be performed. */
public static boolean fill=false;
/** For TestFramework */ public static final String fillDescription = "For testNr==4: A boolean flag deciding whether the source and the destination file should be filled with data before the copy operation will be performed.";
/** Determines if some more output should be done */
public static boolean verbose=false;
/**
* One sector
*/
private static byte[] temp;
/**
* Used inside Test 0.
* @param r RawAccess which is used for the test.
* @param count Number of times the operation is performed.
*/
public static void readSektor0Repeatedly(RawAccess r, int count) {
while (count-->0)
r.read(temp,0);
}
/**
* Used inside Test 1.
* @param r RawAccess which is used for the test.
* @param count Number of times the operation is performed.
*/
public static void readSequentially(RawAccess r, int count) {
for (int i=0; i<count; i++)
r.read(temp,i);
}
/**
* Used inside Test 5. Does not work as expected, because
* Java obviously performs a synchronization before
* entering native code.
* @param r RawAccess which is used for the test.
* @param count Number of times the operation is performed.
*/
public static void readSequentiallyTwoThreads(final RawAccess r, final RawAccess r2, int count) {
final AsynchronousChannel channel = new AsynchronousChannel();
final AsynchronousChannel channel2 = new AsynchronousChannel();
new Thread() {
public void run() {
while (true) {
Integer i = (Integer) channel.take();
if (i==null)
break;
yield();
r2.read(temp,i.intValue());
System.out.println(i);
}
channel2.put(null);
}
}.start();
for (int i=0; i<count; i+=2) {
if (i+1<count)
channel.put(new Integer(i+1));
r.read(temp,i);
System.out.println(i);
}
channel.put(null);
channel2.take();
}
/**
* Used inside Test 2.
* @param r RawAccess which is used for the test.
* @param stepwide Size of a seek.
*/
public static void readSeekForwardTest(RawAccess r, int stepwide) {
long sec = r.getNumSectors();
long position = 0;
while(position<sec) {
r.read(temp,position);
position += stepwide;
}
}
/**
* Used inside Test 2.
* @param r RawAccess which is used for the test.
* @param stepwide Size of a seek.
*/
public static void readSeekForwardFrom0Test(RawAccess r, int stepwide) {
long sec = r.getNumSectors();
long position = 0;
while(position<sec) {
r.read(temp,0);
r.read(temp,position);
position += stepwide;
}
}
/**
* Currently not needed inside any test.
* @param r RawAccess which is used for the test.
* @param numSeeks Number of seeks.
*/
public static void readSeekPseudoRandomTest(RawAccess r, int numSeeks) {
long sec = r.getNumSectors();
long position = 0;
long runNr=0;
while( (numSeeks--) != 0 ) {
r.read(temp,position);
runNr++;
position += (long) (59.2337192*(sec*runNr*runNr));
position %= sec;
}
}
/**
* Used inside Test 3.
* @param r RawAccess which is used for the test.
* @param numSeeks Number of seeks.
* @param random Random number generator which is used for the test.
*/
public static void readRandomSeekTest(RawAccess r, int numSeeks, DiscreteRandomWrapper random){
long sec = r.getNumSectors();
long position = 0;
while( (numSeeks--) != 0 ) {
r.read(temp,position);
position += random.nextInt();
position %= sec;
}
}
/**
* Opens a raw access according to the parameters.
* @param fn Name of the RawDevice.
* @param sectorSize Size of each sector of the RawDevice.
* @param setCacheState Determines if the cache state has to be set.
* @return The RawDevice matching the parameters.
*/
public static RawAccess open(String fn, int sectorSize, boolean setCacheState) {
RawAccess r;
if (useNative) {
NativeRawAccess rn = new NativeRawAccess(fn,sectorSize);
if (verbose) {
System.out.println("Construct a NativeRawAccess");
System.out.println("Native mode: "+rn.mode);
System.out.println("Current harddrive cache mode: "+rn.getHardDriveCacheMode());
}
if (setCacheState) {
System.out.println("Testing cache capabilities of the hard drive");
for (int i=0; i<4; i++) {
System.out.print("Try to set the cache mode to "+i+": ");
rn.setHardDriveCacheMode(i);
int realCacheMode = rn.getHardDriveCacheMode();
if (realCacheMode==i)
System.out.println("ok");
else
System.out.println("failure. Real value is "+realCacheMode);
}
System.out.println("Try to set the cache mode to the mode for the test");
rn.setHardDriveCacheMode(hardDiskCacheMode);
System.out.println("Harddrive cache mode: "+rn.getHardDriveCacheMode());
}
r = rn;
}
else
r = new RAFRawAccess(fn,useSync,sectorSize);
if (makeStatistic)
r = new StatisticsRawAccess(r,50,1.1,1.1);
return r;
}
/**
* Main method
* @param args Command line options are ignored here.
*/
public static void main(String[] args) {
if (!TestFramework.processParameters("RawAccessTest\n", RawAccessTest.class, args, System.out))
return;
RawAccess src=null,dest=null;
Timer timer = null;
long tzero=0;
long tticks=0;
if (testNr!=-1) {
src = open(fn1,startSectorSize, true);
temp=new byte[startSectorSize];
System.out.println(src);
timer = (Timer) TimerUtils.FACTORY_METHOD.invoke();
TimerUtils.warmup(timer);
tzero = TimerUtils.getZeroTime(timer);
tticks = timer.getTicksPerSecond() / 1000; // calculation is in ms now
System.out.println("Timer info: " + timer.timerInfo());
}
if (testNr==0) {
timer.start();
readSektor0Repeatedly(src,numberOfChunks);
long t2 = timer.getDuration();
System.out.println("Duration for "+numberOfChunks+" src.read(temp,0): "+(t2-tzero)/tticks+"ms");
}
else if (testNr==1) {
int sectorSize = startSectorSize;
do {
timer.start();
readSequentially(src,numberOfChunks);
long t2 = timer.getDuration();
double time = ((double) (t2-tzero))/tticks;
double transferRate = ((sectorSize*numberOfChunks)/(time/1000))/1024;
System.out.print("Sector size:\t"+sectorSize+"\t");
System.out.print("Duration:\t"+time+"\t");
System.out.println("Transfer rate in KB/s:\t"+transferRate);
src.close();
sectorSize += stepSectorSize;
src = open(fn1, sectorSize, false);
temp=new byte[sectorSize];
} while (sectorSize<=endSectorSize);
}
else if (testNr==2) {
timer.start();
if (seekFrom0)
readSeekForwardFrom0Test(src,stepwide);
else
readSeekForwardTest(src,stepwide);
long t2 = timer.getDuration();
System.out.println("Duration: "+(t2-tzero)/tticks+"ms");
System.out.println("Number of seek operations: "+(int) (src.getNumSectors()/stepwide));
}
else if (testNr==3) {
xxl.core.util.random.DiscreteRandomWrapper drw;
final long sec = src.getNumSectors();
if (randomJava)
drw = new xxl.core.util.random.JavaDiscreteRandomWrapper();
else {
drw =
new xxl.core.util.random.DiscreteRandomWrapper() {
private long runNr = 0;
public int nextInt() {
runNr++;
return (int) (59.2337192*(sec*runNr*runNr));
}
};
}
timer.start();
readRandomSeekTest(src,numSeeks,drw);
long t2 = timer.getDuration();
System.out.println("Duration: "+(t2-tzero)/tticks+"ms");
System.out.println("Number of seek operations: "+numSeeks);
}
else if (testNr==4) {
if (construct) {
// System.out.println("Construct #1: "+RawAccessUtils.createFileForRaw(fn1,numberOfChunks));
System.out.println("Construct #2: "+RawAccessUtils.createFileForRaw(fn2,numberOfChunks));
}
dest = open(fn2, startSectorSize, true);
if (fill) {
RawAccessUtils.fillRawAccess(src, -1, 1);
RawAccessUtils.fillRawAccess(dest, -1, 2);
}
((StatisticsRawAccess)src).resetCounter();
((StatisticsRawAccess)dest).resetCounter();
timer.start();
RawAccessUtils.copyRawAccess(src,dest);
long t2 = timer.getDuration();
System.out.println("Copy, Sektoren: "+numberOfChunks+"\t"+(t2-tzero)/tticks+"ms");
dest.close();
System.out.println(dest);
}
else if (testNr==5) {
RawAccess src2 = open(fn1,startSectorSize, true);
timer.start();
readSequentiallyTwoThreads(src, src2, numberOfChunks);
long t2 = timer.getDuration();
double time = ((double) (t2-tzero))/tticks;
double transferRate = ((startSectorSize*numberOfChunks)/(time/1000))/1024;
System.out.print("Sector size:\t"+startSectorSize+"\t");
System.out.print("Duration:\t"+time+"\t");
System.out.println("Transfer rate in KB/s:\t"+transferRate);
}
if (testNr!=-1) {
src.close();
System.out.println(src);
}
}
}