2008-03-05 09:52:00 -05:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Microsoft Research Singularity
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
|
|
|
using Microsoft.Singularity;
|
|
|
|
using Microsoft.Singularity.Directory;
|
|
|
|
using Microsoft.Singularity.V1.Services;
|
|
|
|
using Microsoft.Singularity.Io;
|
|
|
|
using Microsoft.Singularity.Channels;
|
|
|
|
|
|
|
|
using Microsoft.Contracts;
|
|
|
|
using Microsoft.SingSharp.Reflection;
|
|
|
|
using Microsoft.Singularity.Applications;
|
|
|
|
using Microsoft.Singularity.Io;
|
|
|
|
using Microsoft.Singularity.Configuration;
|
2008-11-17 18:29:00 -05:00
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
[assembly: Transform(typeof(ApplicationResourceTransform))]
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
[ConsoleCategory(HelpMessage="Disk r/w performance test", DefaultAction=true)]
|
2008-11-17 18:29:00 -05:00
|
|
|
internal class Parameters
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
[InputEndpoint("data")]
|
|
|
|
public readonly TRef<UnicodePipeContract.Exp:READY> Stdin;
|
|
|
|
|
|
|
|
[OutputEndpoint("data")]
|
|
|
|
public readonly TRef<UnicodePipeContract.Imp:READY> Stdout;
|
|
|
|
|
|
|
|
[StringParameter( "device", Mandatory=true, Position=0 , HelpMessage="Device to read/write")]
|
|
|
|
internal string deviceName;
|
|
|
|
|
|
|
|
[LongParameter( "b", Default=0, Mandatory=true, HelpMessage="Set block size to <n> bytes.")]
|
|
|
|
internal long blockBytes;
|
|
|
|
|
|
|
|
[LongParameter( "g", Default=1, HelpMessage="Repeat test n times.")]
|
|
|
|
internal long repeatCount;
|
|
|
|
|
|
|
|
[LongParameter( "l", Default=0, HelpMessage="Fix disk size limit at <n> megabytes.")]
|
|
|
|
internal long diskLimitMB;
|
|
|
|
|
|
|
|
[LongParameter( "m", Default=0, HelpMessage="Perform test with <n> megabytes of data.")]
|
|
|
|
internal long dataMB;
|
|
|
|
|
|
|
|
[LongParameter( "n", Default=0, HelpMessage="Perform test with <n> blocks of data.")]
|
|
|
|
internal long blockCount;
|
|
|
|
|
|
|
|
[LongParameter( "p", Default=0, HelpMessage="Pause <n> seconds between iterations")]
|
|
|
|
internal long pauseSecs;
|
|
|
|
|
|
|
|
[BoolParameter( "r", Default=false, HelpMessage="Perform disk read test.")]
|
|
|
|
internal bool doRead ;
|
|
|
|
|
|
|
|
[BoolParameter( "w", Default=false, HelpMessage="Perform disk write test.")]
|
|
|
|
internal bool doWrite;
|
|
|
|
|
|
|
|
[BoolParameter( "x", Default=false, HelpMessage="Perform test with random I/O offsets.")]
|
|
|
|
internal bool randomIO;
|
|
|
|
|
|
|
|
reflective internal Parameters();
|
|
|
|
|
|
|
|
internal int AppMain() {
|
|
|
|
return DiskBenchmark.AppMain(this);
|
|
|
|
}
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
public class DiskBenchmark
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
internal const uint BytesPerSector = 512;
|
|
|
|
internal const uint SectorsPerMB = 1024 * 1024 / BytesPerSector;
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
public static DiskDeviceContract.Imp:Ready OpenDevice(String! devname)
|
|
|
|
{
|
|
|
|
DiskDeviceContract.Exp! exp;
|
|
|
|
DiskDeviceContract.Imp! imp;
|
|
|
|
DiskDeviceContract.NewChannel(out imp, out exp);
|
|
|
|
DirectoryServiceContract.Imp ns;
|
|
|
|
|
|
|
|
ErrorCode errorOut;
|
|
|
|
// get NS endpoint
|
|
|
|
ns = DirectoryService.NewClientEndpoint();
|
|
|
|
bool success;
|
|
|
|
success = SdsUtils.Bind(devname,ns, exp, out errorOut);
|
2008-11-17 18:29:00 -05:00
|
|
|
if (!success) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Console.WriteLine("Bind of {0} failed. Reason:{1}\n",
|
2008-11-17 18:29:00 -05:00
|
|
|
devname, SdsUtils.ErrorCodeToString(errorOut));
|
2008-03-05 09:52:00 -05:00
|
|
|
delete imp;
|
|
|
|
delete ns;
|
|
|
|
return null;
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
switch receive {
|
|
|
|
case imp.Success():
|
|
|
|
break;
|
|
|
|
case imp.ContractNotSupported():
|
|
|
|
Console.WriteLine("{0} does not support DiskDevice", devname);
|
|
|
|
delete imp;
|
|
|
|
delete ns;
|
|
|
|
return null;
|
|
|
|
case imp.ChannelClosed():
|
|
|
|
Console.WriteLine("DiskDevice channel to {0} closed unexpectedly", devname);
|
|
|
|
delete imp;
|
|
|
|
delete ns;
|
|
|
|
return null;
|
|
|
|
}
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
delete ns;
|
|
|
|
return imp;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
internal static ulong GetRandomOffset(SRandom! rng,
|
2008-03-05 09:52:00 -05:00
|
|
|
ulong diskSectorCount,
|
|
|
|
ulong blockBytes)
|
|
|
|
{
|
|
|
|
ulong maxBlock = diskSectorCount * 512 / blockBytes;
|
|
|
|
ulong offset = 0;
|
2008-11-17 18:29:00 -05:00
|
|
|
for (int i = 0; i < 64; i += 30) {
|
2008-03-05 09:52:00 -05:00
|
|
|
offset ^= (ulong)(rng.Next() << i);
|
|
|
|
}
|
|
|
|
offset &= 0x7fffffffffffffff;
|
|
|
|
offset %= maxBlock;
|
|
|
|
return offset * blockBytes;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
internal static void DisplayPerf(bool didRead,
|
2008-03-05 09:52:00 -05:00
|
|
|
bool didRandom,
|
|
|
|
TimeSpan elapsed,
|
|
|
|
ulong blockCount,
|
|
|
|
ulong blockBytes,
|
|
|
|
ulong nextOffset)
|
|
|
|
{
|
|
|
|
double delta = (double)elapsed.Ticks / TimeSpan.TicksPerSecond;
|
|
|
|
double ops = (double)blockCount / delta;
|
|
|
|
|
|
|
|
string output = String.Format(
|
|
|
|
"{0}{1} {2} {3} Ops/s: {4:f2} MB/s: {5:f2} Elapsed: {6:f2} Check: {7:x}",
|
|
|
|
didRandom ? "R" : "S",
|
|
|
|
didRead ? "R" : "W",
|
|
|
|
blockBytes,
|
|
|
|
blockCount,
|
|
|
|
ops,
|
|
|
|
ops * blockBytes / (1024.0 * 1024.0),
|
|
|
|
delta,
|
|
|
|
nextOffset);
|
|
|
|
|
|
|
|
Console.WriteLine(output);
|
|
|
|
DebugStub.WriteLine(output);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static int DoDevice(string! deviceName,
|
|
|
|
int runNumber,
|
|
|
|
ulong blockCount,
|
|
|
|
ulong blockBytes,
|
|
|
|
ulong diskLimitMB,
|
|
|
|
bool doRandom,
|
|
|
|
bool doRead)
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Open device
|
|
|
|
//
|
|
|
|
DiskDeviceContract.Imp imp = OpenDevice(deviceName);
|
2008-11-17 18:29:00 -05:00
|
|
|
if (null == imp) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Console.WriteLine("Could not open {0}", deviceName);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get disk size
|
|
|
|
//
|
|
|
|
ulong diskSectorCount;
|
|
|
|
|
|
|
|
imp.SendGetSectorCount();
|
2008-11-17 18:29:00 -05:00
|
|
|
switch receive {
|
|
|
|
case imp.AckGetSectorCount(sectorCount):
|
|
|
|
diskSectorCount = sectorCount;
|
|
|
|
break;
|
|
|
|
case imp.ChannelClosed():
|
|
|
|
Console.WriteLine("Channel closed unexpectedly");
|
|
|
|
throw new Exception("Unexpected channel close");
|
|
|
|
}
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
// Constrain disk size if user requested
|
|
|
|
if (diskLimitMB != 0 &&
|
|
|
|
diskSectorCount > diskLimitMB * SectorsPerMB)
|
|
|
|
{
|
|
|
|
diskSectorCount = diskLimitMB * SectorsPerMB;
|
|
|
|
}
|
|
|
|
diskLimitMB = diskSectorCount / SectorsPerMB;
|
|
|
|
|
|
|
|
// Constrain block count if it overflows available blocks
|
2008-11-17 18:29:00 -05:00
|
|
|
if (blockCount * blockBytes > diskSectorCount * BytesPerSector) {
|
2008-03-05 09:52:00 -05:00
|
|
|
blockCount = diskSectorCount * BytesPerSector / blockBytes;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (runNumber == 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Console.WriteLine("# Type: {0} {1}",
|
|
|
|
doRandom ? "Random" : "Sequential",
|
|
|
|
doRead ? "Read" : "Write");
|
|
|
|
Console.WriteLine("# Disk: {0} MB",
|
|
|
|
diskSectorCount / SectorsPerMB);
|
|
|
|
Console.WriteLine("# Limit: {0} MB", diskLimitMB);
|
|
|
|
Console.WriteLine("# Work: {0:f2} MB",
|
|
|
|
blockCount * blockBytes / (1024.0 * 1024.0));
|
|
|
|
Console.WriteLine("# Ops: {0}", blockCount);
|
|
|
|
}
|
|
|
|
|
|
|
|
byte[] in ExHeap buffer = new [ExHeap] byte [blockBytes];
|
|
|
|
|
|
|
|
SRandom rng = new SRandom();
|
|
|
|
|
|
|
|
ulong diskPos = 0;
|
2008-11-17 18:29:00 -05:00
|
|
|
if (doRandom) {
|
2008-03-05 09:52:00 -05:00
|
|
|
diskPos = GetRandomOffset(rng, diskSectorCount, blockBytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
long startIrqCount = ProcessService.GetKernelInterruptCount();
|
|
|
|
long startSwitchCount = ProcessService.GetContextSwitchCount();
|
|
|
|
long startKernelGcCount = ProcessService.GetKernelGcCount();
|
|
|
|
int startGcCount;
|
|
|
|
long startGcMillis;
|
|
|
|
long startGcBytes;
|
|
|
|
GC.PerformanceCounters(out startGcCount,
|
|
|
|
out startGcMillis,
|
|
|
|
out startGcBytes);
|
|
|
|
|
|
|
|
TimeSpan startTime = ProcessService.GetUpTime();
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
for (ulong i = 0; i < blockCount; i++) {
|
2008-03-05 09:52:00 -05:00
|
|
|
ulong sector = diskPos / BytesPerSector;
|
|
|
|
|
|
|
|
byte[]! in ExHeap outBuffer;
|
2008-11-17 18:29:00 -05:00
|
|
|
if (doRead) {
|
2008-03-05 09:52:00 -05:00
|
|
|
imp.SendRead(buffer, 0, blockBytes, sector);
|
|
|
|
imp.RecvAckRead(out outBuffer);
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
imp.SendWrite(buffer, 0, blockBytes, sector);
|
|
|
|
imp.RecvAckWrite(out outBuffer);
|
|
|
|
}
|
|
|
|
buffer = outBuffer;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (doRandom) {
|
2008-03-05 09:52:00 -05:00
|
|
|
diskPos =
|
|
|
|
GetRandomOffset(rng, diskSectorCount, blockBytes);
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
diskPos += blockBytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
TimeSpan endTime = ProcessService.GetUpTime();
|
|
|
|
|
|
|
|
delete buffer;
|
|
|
|
delete imp;
|
|
|
|
|
|
|
|
DisplayPerf(doRead, doRandom, endTime - startTime,
|
|
|
|
blockCount, blockBytes, diskPos);
|
|
|
|
|
|
|
|
int endGcCount;
|
|
|
|
long endGcMillis;
|
|
|
|
long endGcBytes;
|
|
|
|
GC.PerformanceCounters(out endGcCount,
|
|
|
|
out endGcMillis,
|
|
|
|
out endGcBytes);
|
|
|
|
|
|
|
|
Console.WriteLine("[AppGC :- cnt {0} bytes {1} Kern: ints {2} swi {3} gcs {4}]",
|
2008-11-17 18:29:00 -05:00
|
|
|
endGcCount - startGcCount,
|
|
|
|
endGcBytes - startGcBytes,
|
|
|
|
ProcessService.GetKernelInterruptCount() - startIrqCount,
|
|
|
|
ProcessService.GetContextSwitchCount() - startSwitchCount,
|
|
|
|
ProcessService.GetKernelGcCount() - startKernelGcCount);
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static bool ParseNumber(string! arg,
|
|
|
|
string! name,
|
|
|
|
out ulong value)
|
|
|
|
{
|
|
|
|
// arg should look like "[-/][A-z]:[0-9]"
|
2008-11-17 18:29:00 -05:00
|
|
|
if (arg.Length >= 4) {
|
|
|
|
try {
|
2008-03-05 09:52:00 -05:00
|
|
|
value = UInt64.Parse(arg.Substring(3));
|
|
|
|
return true;
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
catch (FormatException) {
|
|
|
|
}
|
|
|
|
catch (OverflowException) {
|
|
|
|
}
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
Console.WriteLine("Could not parse {0}", name);
|
|
|
|
value = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
internal static void Usage()
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
internal static int AppMain(Parameters! config)
|
|
|
|
{
|
|
|
|
string deviceName = config.deviceName;
|
|
|
|
ulong blockBytes = (ulong) config.blockBytes;
|
|
|
|
ulong blockCount = (ulong) config.blockCount;
|
|
|
|
ulong diskLimitMB = (ulong) config.diskLimitMB;
|
|
|
|
ulong repeatCount = (ulong) config.repeatCount;
|
|
|
|
ulong pauseSecs = (ulong) config.pauseSecs;
|
|
|
|
bool randomIO = config.randomIO;
|
|
|
|
bool diskReadTest = false;
|
|
|
|
bool doWrite = config.doWrite;
|
|
|
|
bool doRead = config.doRead;
|
|
|
|
ulong dataMB = (ulong) config.dataMB;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (blockBytes < 512 || (blockBytes & (blockBytes - 1)) != 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Console.WriteLine("Block size must be a power of two greater than or equal to 512.");
|
|
|
|
Usage();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (doRead == doWrite) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Console.WriteLine("Need to specify either read or write test.");
|
|
|
|
Usage();
|
|
|
|
return -1;
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
else if (doRead) {
|
2008-03-05 09:52:00 -05:00
|
|
|
diskReadTest = true;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (dataMB != 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
blockCount = dataMB * 1024 * 1024 / blockBytes;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
for (int run = 0; run < (int)repeatCount; run++) {
|
|
|
|
if (run != 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
System.Threading.Thread.Sleep(1000 * (int)pauseSecs);
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
Tracing.Log(Tracing.Debug, "Starting diskrw run {0}",
|
|
|
|
(UIntPtr) run);
|
2008-11-17 18:29:00 -05:00
|
|
|
if (DoDevice((!)deviceName, run, blockCount, blockBytes,
|
|
|
|
diskLimitMB, randomIO, diskReadTest) != 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
Tracing.Log(Tracing.Debug, "Ending diskrw run {0}",
|
|
|
|
(UIntPtr) run);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|