singrdk/base/Applications/Benchmarks/diskrw/diskrw.sg

377 lines
13 KiB
Plaintext

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: diskrw.sg
//
// Note:
//
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;
[assembly: Transform(typeof(ApplicationResourceTransform))]
namespace Microsoft.Singularity.Applications.Benchmarks.DiskRW
{
[ConsoleCategory(HelpMessage="Disk r/w performance test", DefaultAction=true)]
internal class Parameters
{
[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);
}
}
public class DiskBenchmark
{
private const uint BytesPerSector = 512;
private const uint SectorsPerMB = 1024 * 1024 / BytesPerSector;
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);
if (!success)
{
Console.WriteLine("Bind of {0} failed. Reason:{1}\n",
devname, SdsUtils.ErrorCodeToString(errorOut));
delete imp;
delete ns;
return null;
}
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;
}
delete ns;
return imp;
}
private static ulong GetRandomOffset(SRandom! rng,
ulong diskSectorCount,
ulong blockBytes)
{
ulong maxBlock = diskSectorCount * 512 / blockBytes;
ulong offset = 0;
for (int i = 0; i < 64; i += 30)
{
offset ^= (ulong)(rng.Next() << i);
}
offset &= 0x7fffffffffffffff;
offset %= maxBlock;
return offset * blockBytes;
}
private static void DisplayPerf(bool didRead,
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);
if (null == imp)
{
Console.WriteLine("Could not open {0}", deviceName);
return 1;
}
//
// Get disk size
//
ulong diskSectorCount;
imp.SendGetSectorCount();
switch receive
{
case imp.AckGetSectorCount(sectorCount):
diskSectorCount = sectorCount;
break;
case imp.ChannelClosed():
Console.WriteLine("Channel closed unexpectedly");
throw new Exception("Unexpected channel close");
}
// 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
if (blockCount * blockBytes > diskSectorCount * BytesPerSector)
{
blockCount = diskSectorCount * BytesPerSector / blockBytes;
}
if (runNumber == 0)
{
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;
if (doRandom)
{
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();
for (ulong i = 0; i < blockCount; i++)
{
ulong sector = diskPos / BytesPerSector;
byte[]! in ExHeap outBuffer;
if (doRead)
{
imp.SendRead(buffer, 0, blockBytes, sector);
imp.RecvAckRead(out outBuffer);
}
else
{
imp.SendWrite(buffer, 0, blockBytes, sector);
imp.RecvAckWrite(out outBuffer);
}
buffer = outBuffer;
if (doRandom)
{
diskPos =
GetRandomOffset(rng, diskSectorCount, blockBytes);
}
else
{
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}]",
endGcCount - startGcCount,
endGcBytes - startGcBytes,
ProcessService.GetKernelInterruptCount() - startIrqCount,
ProcessService.GetContextSwitchCount() - startSwitchCount,
ProcessService.GetKernelGcCount() - startKernelGcCount);
return 0;
}
public static bool ParseNumber(string! arg,
string! name,
out ulong value)
{
// arg should look like "[-/][A-z]:[0-9]"
if (arg.Length >= 4)
{
try
{
value = UInt64.Parse(arg.Substring(3));
return true;
}
catch (FormatException)
{}
catch (OverflowException)
{}
}
Console.WriteLine("Could not parse {0}", name);
value = 0;
return false;
}
static void Usage()
{
}
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;
if (blockBytes < 512 || (blockBytes & (blockBytes - 1)) != 0)
{
Console.WriteLine("Block size must be a power of two greater than or equal to 512.");
Usage();
return -1;
}
if (doRead == doWrite)
{
Console.WriteLine("Need to specify either read or write test.");
Usage();
return -1;
}
else if (doRead)
{
diskReadTest = true;
}
if (dataMB != 0)
{
blockCount = dataMB * 1024 * 1024 / blockBytes;
}
for (int run = 0; run < (int)repeatCount; run++)
{
if (run != 0)
System.Threading.Thread.Sleep(1000 * (int)pauseSecs);
Tracing.Log(Tracing.Debug, "Starting diskrw run {0}",
(UIntPtr) run);
if (DoDevice(deviceName, run, blockCount, blockBytes,
diskLimitMB, randomIO, diskReadTest) != 0)
{
return -1;
}
Tracing.Log(Tracing.Debug, "Ending diskrw run {0}",
(UIntPtr) run);
}
return 0;
}
}
}